简体   繁体   中英

Replace string in the list of string

I want to replace the string "$id" with the next list item string. For example if the list is

var list = new List<string>()
{
    "test",
    "$id",
    "central",
};

then the list will become

"test"
"central"
"central"

Similarly if the list has only 1 item "$id" or "$id" is the last item in the list then it will not be replaced with anything.

I have created the following logic

 int index = list.FindIndex(x => x == "$id");
 if (index < list.Count()-1)
 {
    var newList = list.Select(x => x.Replace(list[index], list[index+1])).ToList();
    list = newList;
 }

Is this the right way of doing it or there is more efficient way of doing it.

Any help or suggestion would be appreciated.

This works for me (provided that the list has at least one element):

list =
    list
        .Skip(1)
        .Zip(list, (x1, x0) => x0 == "$id" ? x1 : x0)
        .Append(list.Last())
        .ToList();

For collections with indexers(Array or List), you can do it in one loop

for(var i = 0; i < values.Length; i++)
{
    if (i > 0 && values[i - 1] == "$id")
    {
        values[i - 1] = values[i];
    }
}

For any type of collection you can use enumerator to "loop" the collection only once and have access to current and previous element.

Approach below supports multiple occurrences of "$id" as well.

public static IEnumerable<string> ReplaceTemplateWithNextValue(
    this IEnumerable<string> source, 
    string template
)
{
    using (var iterator = source.GetEnumerator())
    {
        var previous = default(string);
        var replaceQty = 0;
        while (iterator.MoveNext())
        {
            if (iterator.Current == "$id") replaceQty++;

            if (previous == "$id" && iterator.Current != "$id")
            {
                for (var i = 0; i < replaceQty; i++) yield return iterator.Current;
                replaceQty = 0;
            }

            if (iterator.Current != "$id") yield return iterator.Current;

            previous = iterator.Current;
        }

        if (previous == $"$id")
        {
            for (var i = 0; i < replaceQty; i++) yield return previous;
        }
    }
}    

Usage

var list = new List<string>() { "test", "$id", "central" };

var replaced = list.ReplaceTemplateWithNextValue("$id");
// => { "test", "central", "central" }

Supported cases:

[Fact]
public void TestReplace()
{
    ReplaceId(Enumerable.Empty<string>()).Should().BeEmpty(); // Pass
    ReplaceId(new[] { "one", "two" })
        .Should().BeEquivalentTo(new[] { "one", "two" }); // Pass
    ReplaceId(new[] { "$id", "two" })
        .Should().BeEquivalentTo(new[] { "two", "two" }); // Pass
    ReplaceId(new[] { "one", "$id", "two" })
        .Should().BeEquivalentTo(new[] { "one", "two", "two" }); // Pass
    ReplaceId(new[] { "one", "two", "$id" })
        .Should().BeEquivalentTo(new[] { "one", "two", "$id" }); // Pass
    Replace(new[] { "one", "$id", "$id" })
        .Should().BeEquivalentTo(new[] { "one", "$id", "$id" }); // Pass
}

This seems simpler to me, but maybe doesn't work, or is slow?

int index = list.IndexOf("$id");
list.RemoveAt(index);
list.Insert(index, list[index]); //list[index] was list[index+1] before RemoveAt

// With error checks:
int index = list.IndexOf("$id");
if (index == -1) return; // or return error;
list.RemoveAt(index);
if (index >= list.Count-1) return;  // or return error;
list.Insert(index, list[index+1]);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM