Aadith Ramia
Aadith Ramia

Reputation: 10329

Problems removing elements from a list when iterating through the list

I have a loop that iterates through elements in a list. I am required to remove elements from this list within the loop based on certain conditions. When I try to do this in C#, I get an exception. apparently, it is not allowed to remove elements from the list which is being iterated through. The problem was observed with a foreach loop. Is there any standard way to get around this problem?

Note : One solution I could think of is to create a copy of the list solely for iteration purpose and to remove elements from the original list within the loop. I am looking for a better way of dealing with this.

Upvotes: 20

Views: 18576

Answers (8)

Yuval
Yuval

Reputation: 547

You can iterate with foreach this way:

List<Customer> custList = Customer.Populate();
foreach (var cust in custList.ToList())
{
    custList.Remove(cust);
}

Note: ToList on the list of variables, this iterates through the list created by the ToList but removes the items from the original list.

Hope this helps.

Upvotes: 2

Bradley Smith
Bradley Smith

Reputation: 13601

When using List<T> the ToArray() method helps in this scenario vastly:

List<MyClass> items = new List<MyClass>();
foreach (MyClass item in items.ToArray())
{
    if (/* condition */) items.Remove(item);
}

The alternative is to use a for loop instead of a foreach, but then you have to decrement the index variable whenever you remove an element i.e.

List<MyClass> items = new List<MyClass>();
for (int i = 0; i < items.Count; i++)
{
    if (/* condition */)
    {
        items.RemoveAt(i);
        i--;
    }
}

Upvotes: 23

Michael Baldry
Michael Baldry

Reputation: 2028

Another trick is to loop through the list backwards.. removing an item won't affect any of the items you are going to encounter in the rest of the loop.

I'm not recommending this or anything else though. Everything you need this for can probably be done using LINQ statements to filter the list on your requirements.

Upvotes: 2

TheLukeMcCarthy
TheLukeMcCarthy

Reputation: 2283

The reason you get an error is because you're using a foreach loop. If you think about how a foreach loop works this makes sense. The foreach loop calls the GetEnumerator method on the List. If you where to change the number of elements in the List, the Enumerator the foreach loop holds wouldn't have the correct number of elements. If you removed an element a null exception error would be thrown, and if you added an element the loop would miss an item.

If you like Linq and Lamda expressions I would recommend Darin Dimitrov solution, otherwise I would use the solution provided by Chris Schmich.

Upvotes: 0

LukeH
LukeH

Reputation: 269368

If your list is an actual List<T> then you can use the built-in RemoveAll method to delete items based on a predicate:

int numberOfItemsRemoved = yourList.RemoveAll(x => ShouldThisItemBeDeleted(x));

Upvotes: 15

Chris Schmich
Chris Schmich

Reputation: 29476

You can use integer indexing to remove items:

List<int> xs = new List<int> { 1, 2, 3, 4 };
for (int i = 0; i < xs.Count; ++i)
{
    // Remove even numbers.
    if (xs[i] % 2 == 0)
    {
        xs.RemoveAt(i);
        --i;
    }
}

This can be weird to read and tough to maintain, though, especially if the logic in the loop gets any more complex.

Upvotes: 5

Darin Dimitrov
Darin Dimitrov

Reputation: 1038830

You could use LINQ to replace the initial list by a new list by filtering out items:

IEnumerable<Foo> initialList = FetchList();
initialList = initialList.Where(x => SomeFilteringConditionOnElement(x));
// Now initialList will be filtered according to the condition
// The filtered elements will be subject to garbage collection

This way you don't have to worry about loops.

Upvotes: 5

Albin Sunnanbo
Albin Sunnanbo

Reputation: 47038

The recommended solution is to put all your elements you want to remove in a separate list and after the first loop, put a second loop where you iterate over the remove-list and remove those elements form the first list.

Upvotes: 1

Related Questions