views:

2453

answers:

5

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

+2  A: 

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
+1 for the use of LINQ :)
johnc
+2  A: 

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
+4  A: 

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
Very nice, thanks
johnc
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
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
Exactly what I have done, thanks
johnc
+1  A: 

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
A: 

I appriciate 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!

DC
what happens if there is more than one object that meets the condition?
Jason Miesionczek
@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