Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to remove objects from an Enumerable collection in a loop [duplicate]

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

like image 452
johnc Avatar asked Jan 01 '09 05:01

johnc


4 Answers

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

like image 114
ChrisW Avatar answered Oct 16 '22 12:10

ChrisW


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.

like image 24
Mark Maxham Avatar answered Oct 16 '22 14:10

Mark Maxham


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.

like image 2
Rob Walker Avatar answered Oct 16 '22 12:10

Rob Walker


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.

like image 2
TheSoftwareJedi Avatar answered Oct 16 '22 14:10

TheSoftwareJedi