Modifying Collection when using a foreach loop in c# [duplicate]
Asked Answered
F

3

8

Basically, I would like to remove an item from a list whilst inside the foreach loop. I know that this is possible when using a for loop, but for other purposes, I would like to know if this is achievable using a foreach loop.

In python we can achieve this by doing the following:

a = [1, 2, 3, 4, 5, 6, 7, 8, 9]

for i in a:
    print i

    if i == 1:
        a.pop(1)

This gives the following Output

>>>1
3
4
5
6
7
8
9

But when doing something similar in c#, I get an InvalidOperationException, I was wondering if there was a way of getting around this, without just simply using a for loop.

The code in c# that I used when the exception was thrown:

static void Main(string[] args)
  {
  List<string> MyList = new List<string>(new string[] { "1", "2", "3", "4", "5", "6", "7", "8", "9"});

  foreach (string Item in MyList)
    {
    if (MyList.IndexOf(Item) == 0)
      {
      MyList.RemoveAt(1);
      }

    Console.WriteLine(Item);
    }
  }

Thanks in advance

Frilling answered 14/7, 2009 at 8:49 Comment(0)
S
26

You can't do this. From the docs for IEnumerator<T>:

An enumerator remains valid as long as the collection remains unchanged. If changes are made to the collection, such as adding, modifying, or deleting elements, the enumerator is irrecoverably invalidated and its behavior is undefined.

Alternatives are:

  • Build up a new list of items to remove, then remove them all afterwards
  • Use a normal "for" loop and make sure you're careful about not going over the same element twice or missing any out. (You've said you don't want to do this, but what you're trying to do just won't work.)
  • Build a new collection containing only the elements you want to retain

The last of these alternatives is the LINQ-like solution, where you'd typically write:

var newList = oldList.Where(x => ShouldBeRetained(x)).ToList();

(Where ShouldBeRetained is whatever logic you want, of course.) The call to ToList() is only necessary if you actually want it in a list. This leads to more declarative code which is often easier to read. I can't easily guess what your original loop is meant to do (it seems pretty odd at the moment) whereas if you can express the logic purely in terms of the item, it can be a lot clearer.

Systematics answered 14/7, 2009 at 8:53 Comment(1)
I was mainly wondering if I had missed something somewhere with the foreach instance, but if it's not achievable, at least it's now confirmed! Thanks for the answerFrilling
T
6

If all you need is to remove all items that satisfy a condition you could use the List<T>.RemoveAll method:

List<string> MyList = new List<string>(new string[] { "1", "2", "3", "4", "5", "6", "7", "8", "9" });
MyList.RemoveAll(item => item == "1");

Note that this modifies the initial list.

Tartaglia answered 14/7, 2009 at 9:10 Comment(0)
N
1

You definitely may not change a collection in any way when using a foreach loop on it.

You can use a for loop and manage the index for yourself or make a copy of the collection and as you are looping the original, remove items from the copy that equal the item in the original.

In both cases it's not quite as clear or convenient :).

Nominal answered 14/7, 2009 at 8:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.