55

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?

Chi
  • 163
  • 1
  • 2
  • 12
Ashraf Sayied-Ahmad
  • 1,715
  • 2
  • 18
  • 21
  • 2
    This compiles for you? There's no `Length` property on a list. – Austin Salonen Sep 07 '11 at 21:36
  • Follow that code very closely and think about what it would really do when you removed one item from it.... and what if the very next item was also supposed to be removed? – Andrew Barber Sep 07 '11 at 21:37
  • Hmm really? A foreach would throw an error (you can't change the collection while you are enumerating it). One way around it to not enumerate but loop through it with a for loop like you do in your example (which runs perfectly once you fix the bug data.Length --> data.Count) Little addition: It runs but it is still buggy though since you won't test all elements – Eddy Sep 07 '11 at 21:39
  • If I fix the mistake that you have `Lenght` instead of `Count`, your code seems to work fine. (In fact, it does have a bug, but it really shouldn't throw an exception.) – svick Sep 07 '11 at 21:41

13 Answers13

116

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).

Austin Salonen
  • 49,173
  • 15
  • 109
  • 139
ChrisF
  • 134,786
  • 31
  • 255
  • 325
  • 25
    Or you could iterate forwards and simply not increment the counter if you remove an element. But iterating backwards is probably better. – Gabe Sep 07 '11 at 21:36
  • 1
    I've always heard it to be faster to compare against 0: for(int i=data.Count - 1; i >= 0; i--) – FocusedWolf May 14 '14 at 23:13
  • @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). – Dan Bechard Jul 17 '14 at 13:14
  • > 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_ – Boppity Bop Jul 09 '17 at 16:26
  • 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. – kristianp Nov 30 '18 at 09:04
50

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

data.RemoveAll(elem => elem == "One");
Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
12

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).

Habib
  • 219,104
  • 29
  • 407
  • 436
  • 1
    this fires error if "i" is the first element, i mean 0 – Mert Serimer May 31 '16 at 07:09
  • 1
    @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](https://dotnetfiddle.net/fv0U6Y) – Habib May 31 '16 at 14:11
  • 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. – Guillaume Perrot Sep 15 '17 at 01:18
11

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);
      }
    }
Pushpendra
  • 1,694
  • 14
  • 27
  • 8
    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. – Martin Schneider Mar 23 '18 at 13:51
4

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.

Justin Niessner
  • 242,243
  • 40
  • 408
  • 536
3

Why don't you just simply decrement the iterator variable?

var data=new List<string>(){"One","Two","Three"};
for(int i=0 ; i<data.Count ; i++){
  if(data[i]=="One"){
    data.RemoveAt(i);
    i--; // <<<<<<<<<<<
  }
}
Pewpew
  • 61
  • 5
  • I think there would be a bug in i – Greenlight Sep 18 '22 at 11:08
  • @Greenlight: Almost a year after [my reply](https://stackoverflow.com/a/24430128/3779243) the user @Habib [answered](https://stackoverflow.com/a/29659481/3779243) pretty much the same. There is also a person [indicating an error with sole items and empty lists](https://stackoverflow.com/questions/7340757/c-sharp-list-removing-items-while-looping-iterating/24430128#comment62567588_29659481). Neither that person nor you offer any explanation regarding that claim. Please provide further details, since I can see no error, neither with `new List(){"One"}` nor with `new List(){}`. – Pewpew Sep 19 '22 at 19:23
  • I checked it again and I think it is correct. Well, I'm not sure, I'm sorry. :) – Greenlight Sep 22 '22 at 08:27
1

Here is a dirty trick and I wonder what the critique of it will be?

var data=new List<string>(){"One","Two","Three"};
foreach (string itm in (data.ToArray()))
{
  if string.Compare(name, "one", true) == 0) data.Remove(name);
}
B H
  • 1,730
  • 18
  • 24
  • 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. – Puppy Mar 20 '15 at 12:47
  • 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. – Sorensen Jul 01 '16 at 13:57
  • And, as mentioned in an [earlier reply](https://stackoverflow.com/a/38269808/4858), copies every element. – skst Sep 06 '22 at 23:36
1
var data=new List<string>(){"One","Two","Three"};
for(int i=0; i<data.Count; ){
  if(data[i]=="One") data.RemoveAt(i);
  else ++i;
}
Tutankhamen
  • 3,532
  • 1
  • 30
  • 38
1

The general solution below makes a copy of the list and handles the negative index:

foreach (void item_loopVariable in MyList.ToList) {
    item = item_loopVariable;

}
alwaysVBNET
  • 3,150
  • 8
  • 32
  • 65
0

You can use Stack class

        Stack<string> myStack = new Stack<string>();

        foreach (var item in Enumerable.Range(1,1001))
            myStack.Push("Str " + item.ToString());

        while (myStack.Any())
            Console.WriteLine("Now {0} items in Stack, removed item is {1}",myStack.Count,myStack.Pop());

        Console.ReadKey();
0

I had to remove more than one item from the list. so,I reinitialized the list count. Is there any other better option?

for (int i = dtList.Count - 1; dtList.Count > 0; )
{
     DateTime tempDate = dtList[i].Item1.Date;
     var selectDates = dtList.FindAll(x => x.Item1.Date == tempDate.Date);
     selectDates.Sort((a, b) => a.Item1.CompareTo(b.Item1));
     dtFilteredList.Add(Tuple.Create(selectDates[0].Item1, selectDates[0].Item2));
     dtList.RemoveAll(x => x.Item1.Date == tempDate.Date);
     i = dtList.Count - 1;
}
Madhu
  • 41
  • 5
0
        var data = new List<string>() { "One", "Two", "Three" };
        data.RemoveAll(p=>p=="One");
Nivid Dholakia
  • 5,272
  • 4
  • 30
  • 55
-2
List<string> list = new List<string>();
     list.Add("sasa");
     list.Add("sames");
     list.Add("samu");
     list.Add("james");
     for (int i = list.Count - 1; i >= 0; i--)
     {

         list.RemoveAt(i);

    }
Sajidur Rahman
  • 613
  • 6
  • 9