C# List - Removing items while looping / iterating
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.
Comments
-
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 over 12 yearsOr you could iterate forwards and simply not increment the counter if you remove an element. But iterating backwards is probably better.
-
Chris Marisic over 12 yearsFWIW this is a
List<T>
operation only. -
Ashraf Sayied-Ahmad over 12 yearsThis 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 over 12 years@user, nowhere in your code sample are you using an enumerator.
-
Ashraf Sayied-Ahmad over 12 yearsEnumerator as a concept.
-
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 almost 10 yearsI've always heard it to be faster to compare against 0: for(int i=data.Count - 1; i >= 0; i--)
-
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 fieldCount
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 about 9 yearsI 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 over 8 yearsYou should format your answer.
-
Mert Serimer almost 8 yearsthis fires error if "i" is the first element, i mean 0
-
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 return0
and the effect of decrement would be visible on next usage ofi
. Try the code in VS or in the linked fiddle -
Sorensen almost 8 yearsThis 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 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 over 6 yearsActually, 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 about 6 yearsnote 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 over 5 yearsFor 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.