Swift: Better way to remove a specific Object from an array?
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.
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.