1

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.

3
  • What about scenario where collection has two "$id" one after another? Commented Feb 19, 2021 at 4:04
  • no there will be only 1 "$id" in the list @Fabio Commented Feb 19, 2021 at 4:06
  • 1
    In that case, it looks like you only need list[index] = list[index + 1] inside the if block. Also, use list.IndexOf("$id") instead of FindIndex() with a predicate. Commented Feb 19, 2021 at 4:15

3 Answers 3

2

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();
Sign up to request clarification or add additional context in comments.

Comments

1

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
}

5 Comments

Why did you delete your comment on my answer?
@Enigmativity, sorry, you updated the question and I decided that comment become irrelevant.
I always feel like it makes it hard to understand the other comments. Now my reply to you is confusing.
@Enigmativity, so far I was under impression that here is an "unspoken" convention: when you make some "low importance" comment about others code and they addressed that comment by updating the code - you just remove your own comment and they remove their.
I dislike needing to weed through past comments to see how I can keep them semantically valid. It's a big waste of time IMHO.
0

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]);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.