Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift: Better way to remove a specific Object from an array?

Tags:

arrays

swift

I'm trying to be more efficient with my code, but I'm having a brain fart. This code I wrote works great, and does exactly what I need it to do: It checks an array and removes an Object at an unknown Index. But I feel there is a better, more efficient way to write it. I went to Array.remove(at:) but that requires a known index. I'm getting into big O notation and don't know how to make this easier to process. Any ideas?

 // create a new object array
 var sort : [MyCustomObject] = []

//iterate through my object array  

        for i in objectArray{
            if i === objectToRemove{
            }
            else{
                sort.append(i)
            }
        }

     // set old array to sort, which no longer has the unwanted object 
        self.objectArray = sort
like image 882
Brandon Bravos Avatar asked Aug 28 '18 19:08

Brandon Bravos


People also ask

How can I remove a specific object from an array?

If you want to remove an item from an array, you can use the pop() method to remove the last element or the shift() method to remove the first element.

How do I remove a specific item from a list in Swift?

To remove an element from the Swift Array, use array. remove(at:index) method.

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.

How do you remove one property from an array?

To remove a property from all objects in an array:Use the Array. forEach() method to iterate over the array. On each iteration, use the delete operator to delete the specific property.


2 Answers

Use firstIndex(where:) (previously called index(where:) in Swift 4.1 and earlier) to search the array for your object using the predicate { $0 === objectToRemove }, then call remove(at:) on the array to remove it:

if let idx = objectArray.firstIndex(where: { $0 === objectToRemove }) {
    objectArray.remove(at: idx)
}

This allows you to search for your object whether it is Equatable or not.

like image 107
vacawama Avatar answered Dec 04 '22 07:12

vacawama


If you are coding with Xcode 10.0+ beta (Swift 4.2 or later) you can use the new method removeAll(where:)

mutating func removeAll(where predicate: (Element) throws -> Bool) rethrows

Discussion: Use this method to remove every element in a collection that meets particular criteria. Complexity: O(n), where n is the length of the collection.

This example removes all the odd values from an array of numbers:

Swift 5.2 or later

extension BinaryInteger {
    var isEven: Bool { isMultiple(of: 2) }
    var isOdd: Bool { !isMultiple(of: 2) }
}

var numbers = [5, 6, 7, 8, 9, 10, 11]
numbers.removeAll(where: \.isOdd) // numbers == [6, 8, 10]
numbers

In your case make sure MyCustomObject conforms to Equatable

objectArray.removeAll(where: { $0 == objectToRemove })

or use one of its properties that does conform to it as the predicate (i.e id: Int):

objectArray.removeAll(where: { $0.id == idToRemove })

Note: If you are not using Xcode 10.0+ beta (Swift 4.2) you can implement your own removeAll(where:) method as you can see in this answer.


Implementing a removeFirst(where:) and removeLast(where:) to avoid iterating the whole Collection as mentioned in comments by @vacawama

Swift 4.1

extension RangeReplaceableCollection  {
    @discardableResult
    mutating func removeFirst(where predicate: (Element) throws -> Bool) rethrows -> Element?  {
        guard let index = try index(where: predicate) else { return nil }
        return remove(at: index)
    }
}

extension RangeReplaceableCollection where Self: BidirectionalCollection {
    @discardableResult
    mutating func removeLast(where predicate: (Element) throws -> Bool) rethrows -> Element? {
        guard let index = try indices.reversed().first(where: {
            try predicate(self[$0])
        }) else { return nil }
        return remove(at: index)
    }
}

Swift 4.2 or later (as suggested by @Hamish)

extension RangeReplaceableCollection {
    @discardableResult
    mutating func removeFirst(where predicate: (Element) throws -> Bool) rethrows -> Element? {
        guard let index = try firstIndex(where: predicate) else { return nil }
        return remove(at: index)
    }
}

extension RangeReplaceableCollection where Self: BidirectionalCollection {
    @discardableResult
    mutating func removeLast(where predicate: (Element) throws -> Bool) rethrows -> Element? {
        guard let index = try lastIndex(where: predicate) else { return nil }
        return remove(at: index)
    }
}

You can also check this post for a remove(while:), removeLast(while:) and dropLast(while:) method implementations.

like image 34
Leo Dabus Avatar answered Dec 04 '22 06:12

Leo Dabus