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
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
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With