Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Selectively remove and delete objects from a NSMutableArray in Swift

Basic question. What is the best way to selectively remove and delete items from a mutable Array in Swift?

There are options that do NOT seem to be suited for this like calling removeObject inside a

  • for in loop
  • enumeration block

and others that appear to work in general like

  • for loop using an index + calling removeObjectAtIndex, even inside the loop
  • for in loop for populating an arrayWithItemsToRemove and then use originalArray.removeObjectsInArray(arrayWithItemsToRemove)
  • using .filter to create a new array seems to be really nice, but I am not quite sure how I feel about replacing the whole original array

Is there a recommended, simple and secure way to remove items from an array? One of those I mentioned or something I am missing?

It would be nice to get different takes (with pros and cons) or preferences on this. I still struggle choosing the right one.

like image 701
Bernd Avatar asked Sep 06 '14 11:09

Bernd


People also ask

How do I remove something from a list in Swift?

Swift – Remove Element from Array To remove an element from the Swift Array, use array. remove(at:index) method. Remember that the index of an array starts at 0. If you want to remove ith element use the index as (i-1).

Which is method syntax correct to remove item from array Swift?

Swift Array remove() The remove() method removes an element from the array at the specified index.

What is NSMutableArray in Swift?

The NSMutableArray class declares the programmatic interface to objects that manage a modifiable array of objects. This class adds insertion and deletion operations to the basic array-handling behavior inherited from NSArray . NSMutableArray is “toll-free bridged” with its Core Foundation counterpart, CFMutableArray .


2 Answers

If you want to loop and remove elements from a NSMutableArray based on a condition, you can loop the array in reverse order (from last index to zero), and remove the objects satisfying the condition.

For example, if you have an array of integers and want to remove the numbers divisible by three, you can run the loop like this:

var array: NSMutableArray = [1, 2, 3, 4, 5, 6, 7];

for index in stride(from: array.count - 1, through: 0, by: -1) {
    if array[index] as Int % 3 == 0 {
        array.removeObjectAtIndex(index)
    }
}

Looping in reverse order ensures that the index of the array elements still to check doesn't change. In forward mode instead, if you remove for instance the first element, then the element previously at index 1 will change to index 0, and you have to account for that in the code.

Usage of removeObject (which doesn't work with the above code) is not recommended in a loop for performance reasons, because its implementation loops through all elements of the array and uses isEqualTo to determine whether to remove the object or not. The complexity order raises from O(n) to O(n^2) - in a worst case scenario, where all elements of the array are removed, the array is traversed once in the main loop, and traversed again for each element of the array. So all solution based on enumeration blocks, for-in, etc., should be avoided, unless you have a good reason.

filter instead is a good alternative, and it's what I'd use because:

  • it's concise and clear: 1 line of code as opposed to 5 lines (including closing brackets) of the index based solution
  • its performances are comparable to the index based solution - it is a bit slower, but I think not that much

It might not be ideal in all cases though, because, as you said, it generates a new array rather than operating in place.

like image 50
Antonio Avatar answered Sep 30 '22 17:09

Antonio


When working with NSMutableArray you shouldn't remove objects while you are looping along the mutable array itself (unless looping backwards, as pointed out by Antonio's answer).

A common solution is to make an immutable copy of the array, iterate on the copy, and remove objects selectively on the original mutable array by calling "removeObject" or by calling "removeObjectAtIndex", but you will have to calculate the index, since indexes in the original array and the copy will not match because of the removals (you will have to decrement the "removal index" each time an object is removed).

Another solution (better) is to loop the array once, create an NSIndexSet with the indexes of the objects to remove, and then call "removeObjectsAtIndexes:" on the mutable array.

See documentation on NSMutableArray's "removeObjectsAtIndexes:" in Swift.

like image 23
George Avatar answered Sep 30 '22 19:09

George