20

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.

0

8 Answers 8

23

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--;
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

If you're using a List<T> then why not use the built-in RemoveAll method for this scenario?
I shy away from using methods that take delegates as their parameter, because you either have to create a new method or add an anonymous method to the current block. If you do this, your code becomes incompatible with edit-and-continue when debugging.
Why the needless copy operation in ToArray()?
Actually, i've been thinking about this one. Since the generic List class is implemented using arrays, every call to Remove() will carry a large overhead with it. It would actually be more efficient to declare a new List (whose initial capacity was set to the item count in the first List), add every element that doesn't match the removal condition into the new List and then assign the new List to the variable containing the old List.
15

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

2 Comments

this method assumes you have a filter of sorts. not useful when you don't which is the question.
@Lord Black: The question specifically says that items should be removed "based on certain conditions"; my hypothetical ShouldThisItemBeDeleted function would be an expression of those conditions.
5

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.

1 Comment

Or, if the list is a List<T> then you could use the RemoveAll method.
5

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.

Comments

2

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.

Comments

2

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.

3 Comments

Wow.... this 'just worked' right out of the box for me! Completely fixed my problem. Sometimes the bottom of the list has the right answer (right for me at least).
Same here this was the winner for me. Good job.
Thanks guys, scroll down a bit and you will find short and simple answers
1

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.

1 Comment

Recommended by whom? This is a pointlessly expensive approach, for no benefit.
0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.