C# List - Removing items while looping / iterating

77,422

Solution 1

If you need to remove elements then you must iterate backwards so you can remove elements from the end of the list:

var data=new List<string>(){"One","Two","Three"};
for(int i=data.Count - 1; i > -1; i--)
{
    if(data[i]=="One")
    {
        data.RemoveAt(i);
    }
}

However, there are more efficient ways to do this with LINQ (as indicated by the other answers).

Solution 2

You can use List<T>.RemoveAll to handle this:

data.RemoveAll(elem => elem == "One");

Solution 3

You can also use a forward moving loop like:

var data = new List<string>() { "One", "Two", "Three", "One", "One", "Four" };
for (int i = 0; i < data.Count; i++)
{
    if (data[i] == "One")
    {
        data.RemoveAt(i--);
    }
}

This line data.RemoveAt(i--); is stopping the effect of increment in the iteration variable at the end of the loop, in case of item being removed from the list.

It will remove the item from index at the current iteration value and then after removing the item, the iterator would be set to one less value than the current one. At the end of loop, the increment in loop body will move it to next valid index.

Here is a working dotfiddle

(Please note, that I personally use reverse loop for situation like these because IMO, they are easier to understand, this answer here is just for displaying another way of achieving it).

Solution 4

I happen to come across a simple solution for this using foreach and .ToArray()

  var data=new List<string>(){"One","Two","Three"};
   foreach ( var d in data.ToArray()){
      if(d =="One"){
        data.Remove(d);
      }
    }

Solution 5

You could try ChrisF's reverse iteration method to remove your item.

You could also simply:

List.Remove("One");

Or:

List.RemoveAll(i => i == "One"); // removes all instances

And be done with it. There's really no point in iterating over the collection to remove a single item.

Share:
77,422
Ashraf Sayied-Ahmad
Author by

Ashraf Sayied-Ahmad

Senior Software Engineer

Updated on July 22, 2022

Comments

  • Ashraf Sayied-Ahmad
    Ashraf Sayied-Ahmad almost 2 years

    Suppose that I have the following code snippet:

    var data=new List<string>(){"One","Two","Three"};
    for(int i=0 ; i<data.Count ; i++){
      if(data[i]=="One"){
        data.RemoveAt(i);
      }
    }
    

    The following code throws exception.

    My question is what is the best way to avoid this exception and to remove the element while looping?

  • Gabe
    Gabe over 12 years
    Or you could iterate forwards and simply not increment the counter if you remove an element. But iterating backwards is probably better.
  • Chris Marisic
    Chris Marisic over 12 years
    FWIW this is a List<T> operation only.
  • Ashraf Sayied-Ahmad
    Ashraf Sayied-Ahmad over 12 years
    This is good but I need the algorithm that works fine. I understood that looping in reverse manner will not affect the enumerator job and then it will not throw an exception
  • Kirk Woll
    Kirk Woll over 12 years
    @user, nowhere in your code sample are you using an enumerator.
  • Ashraf Sayied-Ahmad
    Ashraf Sayied-Ahmad over 12 years
    Enumerator as a concept.
  • Reed Copsey
    Reed Copsey over 12 years
    @user931589: You're not enumerating - that's not the problem. THe problem in your code is you go out of bounds. Do you need to use a for loop in ascending order for some reason? What is your actual code doing/trying to do?
  • FocusedWolf
    FocusedWolf almost 10 years
    I've always heard it to be faster to compare against 0: for(int i=data.Count - 1; i >= 0; i--)
  • Dan Bechard
    Dan Bechard almost 10 years
    @FocusedWolf Only if you would otherwise be evaluating Count every iteration. Generally, if Count() is a method, it will be faster to only evaluate it once, but if it is a field Count there won't be any difference in performance since it's already cached. If it's a property, it depends on whether the property is accessing a field (cached, fast) or calling a method (calculated, slow).
  • Puppy
    Puppy about 9 years
    I used this technique in C++ fairly often- iterate over a copy whilst mutating the source structure. This reminded me of it at an important time.
  • Broots Waymb
    Broots Waymb over 8 years
    You should format your answer.
  • Mert Serimer
    Mert Serimer almost 8 years
    this fires error if "i" is the first element, i mean 0
  • Habib
    Habib almost 8 years
    @MertSerimer, why do you think it will throw an exception ? did you try the code ? The code above removes the first element or at index 0, and it shouldn't throw the exception. The reason is that i-- will return 0 and the effect of decrement would be visible on next usage of i. Try the code in VS or in the linked fiddle
  • Sorensen
    Sorensen almost 8 years
    This has O(n^2) behavior. The Remove() call has to perform another linear lookup for the object you want removed. I would not recommend this technique.
  • Boppity Bop
    Boppity Bop almost 7 years
    > there are more efficient ways to do this with LINQ (as indicated by the other answers). - anyone actually measured those indicated by others? I don't think so. Since when LINQ became faster than a for loop? hint: amount of SO points does not amount for better knowledge quite often
  • Guillaume Perrot
    Guillaume Perrot over 6 years
    Actually, this is nice to go forward sometimes like if you want to update all subsequent elements in the same iteration, feels more natural than thinking backwards.
  • Martin Schneider
    Martin Schneider about 6 years
    note that .toArray() creates a full copy of the list. So there is an impact on performance and memory consumption when having a heavy list.
  • kristianp
    kristianp over 5 years
    For large lists, the LINQ may be faster, because RemoveAt is O(n) in the size of the list. So the algorithm is O(n*m), where m is the number of items to remove.