Andrew Grant Andrew Grant - 2 months ago 16
Objective-C Question

Best way to remove from NSMutableArray while iterating?

In Cocoa, if I want to loop through an NSMutableArray and remove multiple objects that fit a certain criteria, what's the best way to do this without restarting the loop each time I remove an object?


Edit: Just to clarify - I was looking for the best way, e.g. something more elegant than manually updating the index I'm at. For example in C++ I can do;

iterator it = someList.begin();

while (it != someList.end())
if (shouldRemove(it))
it = someList.erase(it);


For clarity I like to make an initial loop where I collect the items to delete. Then I delete them. Here's a sample using Objective-C 2.0 syntax:

NSMutableArray *discardedItems = [NSMutableArray array];
SomeObjectClass *item;

for (item in originalArrayOfItems) {
    if ([item shouldBeDiscarded])
        [discardedItems addObject:item];

[originalArrayOfItems removeObjectsInArray:discardedItems];

Then there is no question about whether indices are being updated correctly, or other little bookkeeping details.

Edited to add:

It's been noted in other answers that the inverse formulation should be faster. i.e. If you iterate through the array and compose a new array of objects to keep, instead of objects to discard. That may be true (although what about the memory and processing cost of allocating a new array, and discarding the old one?) but even if it's faster it may not be as big a deal as it would be for a naive implementation, because NSArrays do not behave like "normal" arrays. They talk the talk but they walk a different walk. See a good analysis here:

The inverse formulation may be faster, but I've never needed to care whether it is, because the above formulation has always been fast enough for my needs.

For me the take-home message is to use whatever formulation is clearest to you. Optimize only if necessary. I personally find the above formulation clearest, which is why I use it. But if the inverse formulation is clearer to you, go for it.