I am using block based enumeration similar to the following code:
[[[rows objectForKey:self.company.coaTypeCode] objectForKey:statementType]
enumerateObjectsWithOptions:NSEnumerationConcurrent
usingBlock:^(id coaItem, NSUInteger idx, BOOL *stop) {
// block code here
}]
I would like to remove some of the objects during the enumeration process depending on the their object values.
How could I do this? I know that manipulating an mutable array or dictionary (NSMutableArray or NSMutableDictionary) during enumeration is usually not possible.
What would be the best way to implement this?
Thank you!
Since you can't remove objects from an array or dictionary during enumeration, you'll have to accumulate the items you want to delete, and then delete them all after the enumeration.
If you're dealing with an array, you can just accumulate the indices.:
Since the order of the keys in an NSDictionary is undefined, for an NSMutableDictionary you'll have to accumulate keys instead:
It's the same thing if you're enumerating with a block. Declare the enumerator in the same scope where you declare the block and it will be retained and just work.
Also worth looking at this question from 3 years ago: Best way to remove from NSMutableArray while iterating?.
Another option, with an array, is to use a conventional
for
loop, with the array'scount
as the limit. Then one needs to be cognizant of whether an element is removed from a location<=
the index (in which case the index should be decremented) or>
than the index (in which case the index is left unmodified other than thefor
statement's increment).For a dictionary you can first create an array with
allKeys
, and then iterate through the array. In this case no fiddling with index values is required.Whether you build up an index set during enumeration, or modify the array itself during enumeration, you will have to give up
NSEnumerationConcurrent
, because most Cocoa objects cannot safely be modified simultaneously from multiple threads.Anyway, the simplest (but maybe not most efficient) approach is to just enumerate a copy of the container.
For an array, you can enumerate a copy in reverse. I assume that as each item is being enumerated, you may decide to remove that item, but not other items previously enumerated or yet to be enumerated.
You have to enumerate the array in reverse to avoid changing the not-yet-enumerated part of the array.
For a dictionary, you can just enumerate a copy with no special options: