Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Efficiently deleting item from within 'foreach'

For now, the best I could think of is:

bool oneMoreTime = true;
while (oneMoreTime)
{
    ItemType toDelete=null;
    oneMoreTime=false;
    foreach (ItemType item in collection)
    {
        if (ShouldBeDeleted(item))
        {
            toDelete=item;
            break;
        }
    }
    if (toDelete!=null)
    {
        collection.Remove(toDelete);
        oneMoreTime=true;
    }
}

I know that I have at least one extra variable here, but I included it to improve the readability of the algorithm.

like image 986
Daniel Mošmondor Avatar asked Jan 09 '12 16:01

Daniel Mošmondor


1 Answers

The "RemoveAll" method is best.

Another common technique is:

var itemsToBeDeleted = collection.Where(i=>ShouldBeDeleted(i)).ToList();
foreach(var itemToBeDeleted in itemsToBeDeleted)
    collection.Remove(itemToBeDeleted);

Another common technique is to use a "for" loop, but make sure you go backwards:

for (int i = collection.Count - 1; i >= 0; --i)
    if (ShouldBeDeleted(collection[i]))
        collection.RemoveAt(i);

Another common technique is to add the items that are not being removed to a new collection:

var newCollection = new List<whatever>();
foreach(var item in collection.Where(i=>!ShouldBeDeleted(i))
    newCollection.Add(item);

And now you have two collections. A technique I particularly like if you want to end up with two collections is to use immutable data structures. With an immutable data structure, "removing" an item does not change the data structure; it gives you back a new data structure (that re-uses bits from the old one, if possible) that does not have the item you removed. With immutable data structures you are not modifying the thing you're iterating over, so there's no problem:

var newCollection = oldCollection;
foreach(var item in oldCollection.Where(i=>ShouldBeDeleted(i))
    newCollection = newCollection.Remove(item);

or

var newCollection = ImmutableCollection<whatever>.Empty;
foreach(var item in oldCollection.Where(i=>!ShouldBeDeleted(i))
    newCollection = newCollection.Add(item);

And when you're done, you have two collections. The new one has the items removed, the old one is the same as it ever was.

like image 119
Eric Lippert Avatar answered Oct 10 '22 21:10

Eric Lippert