13

Duplicate

Modifying A Collection While Iterating Through It


Has anyone a nice pattern to allow me to get around the inability to remove objects while I loop through an enumerable collection (eg, an IList or KeyValuePairs in a dictionary)

For example, the following fails, as it modifies the List being enumerated over during the foreach

foreach (MyObject myObject in MyListOfMyObjects)
{
     if (condition) MyListOfMyObjects.Remove(myObject);
}

In the past I have used two methods.

I have replaced the foreach with a reversed for loop (so as not to change the any indexes I am looping over if I remove an object).

I have also tried storing a new collection of objects to remove within to loop, then looping through that collection and removed the objects from the original collection.

These work fine, but neither feels nice, and I was wondering if anyone has come up with a more elegant solution to the issue

Community
  • 1
  • 1
johnc
  • 39,385
  • 37
  • 101
  • 139
  • I appear unable to close my own post as SO thinks I am trying to vote on it. Happy for someone else to. – johnc Jan 01 '09 at 05:40
  • No problem. I spend several minutes searching SO before posting a question. The original question is one I remember, but was not very well titled. I edited it so it should be easier to find in the future: How to modify or delete items from an enumerable collection while iterating through it in C# – Jason Jackson Jan 01 '09 at 05:47

7 Answers7

13

There's a useful List<T>.RemoveAll(Predicate<T> match) method which I think is designed for this: http://msdn.microsoft.com/en-us/library/wdka673a.aspx

ChrisW
  • 54,973
  • 13
  • 116
  • 224
  • Edited answer even nicer :), although my current requirement is to remove items from a dictionary which doesn't have either RemoveRange or RemoveAll, so I will just do the double loop method – johnc Jan 01 '09 at 05:46
  • If it's a dictionary, perhaps use the dictionary's keys to initialize a list or array (i.e. copy all the keys into a second collection): and then iterate that second collection, removing entries from the dictionary. – ChrisW Jan 01 '09 at 05:54
  • Exactly what I have done, thanks – johnc Jan 01 '09 at 05:54
8

It's kind of simple-minded, but when I plan to delete items from an IEnumerable/IList I usually just make a copy:

foreach (MyObject myObject in new List<MyObject>(MyListOfMyObjects))
{
     if (condition) MyListOfMyObjects.Remove(myObject);
}

It's not the most efficient way to do it, but it's easy to read. Premature optimization and all that.

Mark Maxham
  • 1,501
  • 2
  • 13
  • 19
2

Do the inverse, creating a new list:

List myFilteredList = new List();
foreach (MyObject myObject in myListOfMyObjects)
{
     if (!condition) myFilteredList.Add(myObject);
}

Then use the new list wherever you need it.

You can also use a LINQ expression easily, again, inversing the condition. This has the added benefit of not creating a new structure, but also the pitfalls of it being a lazy enumerable:

var myFilteredList = from myObject in myListOfMyObjects
                     where !condition
                     select myObject;

However, if you truly need to remove the items from the list, I typically use the "create a new list, then reiterate and remove" approach.

TheSoftwareJedi
  • 34,421
  • 21
  • 109
  • 151
2

I don't like the reversed for loop idea, since that only works on certain data structures.

In general I'd use the second technique and accumulate the items to be deleted in a separate 'to-be-deleted' collection. If deletion can cause existing iterates to be invalidated (as will happen with any balanced tree collection for example) then I don't see a way around this.

The only other technique that I've occasionally used is to restart the whole iteration when you find the first element to delete. If you make it through without finding any items to delete then the function is finished. This is inefficient, but sometimes neccessary if deleting one item from the collection may change the set of items that need to be deleted.

Rob Walker
  • 46,588
  • 15
  • 99
  • 136
2

I just came across this post and thought I would share.

void RemoveAll(object condition)  
{

    bool found = false;

    foreach(object thisObject in objects)    
    {

        if (condition)    
        {    
            objects.Remove(thisObject);

            found = true;

            break; //exit loop    
        }     
     }

    // Call again recursively

    if (found) RemoveAll(condition);

}
johnc
  • 39,385
  • 37
  • 101
  • 139
calvin
  • 21
  • 1
1

I have a dictionary and want to Dispose of all Values. When each value is disposed it removes itself from the dictionary which creates the problem you discuss. I did the following:

foreach (var o in dictionary.Values.ToList())
{
  o.Dispose();
}
0

I appreciate this may be dead now but the way I always do this is:

foreach (MyObject myObject in MyListOfMyObjects)
{

if (condition) MyListOfMyObjects.Remove(myObject);

break;

}

Object is removed and then loop exits, viola!

BenMorel
  • 34,448
  • 50
  • 182
  • 322
DC.
  • 9
  • 1
  • what happens if there is more than one object that meets the condition? – Jason Miesionczek Apr 29 '10 at 17:19
  • @DC No worries regarding it being dead, I'm always interested in this one, but Jason's comment is valid. Break is only valid if there is a single item to be modified. Thanks for your contribution though. – johnc Apr 29 '10 at 23:16