What is a Swift array equivalent to NSMutableArray
\'s -removeObjectsAtIndexes:
? Removing each index one by one doesn\'t work, as remaining indexes
Here is the solution I currently use:
extension Array {
mutating func removeObjectAtIndexes(indexes: [Int]) {
var indexSet = NSMutableIndexSet()
for index in indexes {
indexSet.addIndex(index)
}
indexSet.enumerateIndexesWithOptions(.Reverse) {
self.removeAtIndex($0.0)
return
}
}
mutating func removeObjectAtIndexes(indexes: Int...) {
removeObjectAtIndexes(indexes)
}
}
Swift 4 attempt
extension Array {
mutating func removeAtIndexes(indexes: IndexSet) {
var i:Index? = indexes.last
while i != nil {
self.remove(at: i!)
i = indexes.integerLessThan(i!)
}
}
}
Based upon Kent's solution but updated for Swift 3
extension Array {
mutating func remove(indices: IndexSet) {
self = self.enumerated().filter { !indices.contains($0.offset) }.map { $0.element }
}
}
I needed this for working with a NSTableview. This is what I use.
extension Array{
mutating func removeElementsAtIndexes(indexset:NSIndexSet){
self = self.enumerate().filter({!indexset.containsIndex($0.index)}).map({$0.element})
}
}
I like a pure Swift solution, i.e. without resorting to NSIndexSet:
extension Array {
mutating func removeAtIndexes (ixs:[Int]) -> () {
for i in ixs.sorted(>) {
self.removeAtIndex(i)
}
}
}
EDIT In Swift 4 that would be:
extension Array {
mutating func remove (at ixs:[Int]) -> () {
for i in ixs.sorted(by: >) {
self.remove(at:i)
}
}
}
But years after I wrote that answer, the WWDC 2018 Embracing Algorithms video points out the flaw: it's O(n2), because remove(at:)
itself has to loop through the array.
According to that video, Swift 4.2 removeAll(where:)
is efficient because it uses half-stable partitioning. So we could write something like this:
extension Array {
mutating func remove(at set:IndexSet) {
var arr = Swift.Array(self.enumerated())
arr.removeAll{set.contains($0.offset)}
self = arr.map{$0.element}
}
}
My tests show that despite the repeated contains
, that's 100 times faster. However, @vadian's approach is 10 times faster than that, because he unrolls contains
by ingeniously walking the index set at the same time he walks the array (using half-stable partitioning).
Updated for Swift 2.0:
extension Array {
mutating func removeAtIndices(incs: [Int]) {
incs.sort(>).map { removeAtIndex($0) }
}
}
Use forEach
instead of map
if it gives a warning that the result isn't used (Since Swift 2 beta 6 I think)
EDIT: Super generic lazy solution:
extension RangeReplaceableCollectionType where Index : Comparable {
mutating func removeAtIndices<S : SequenceType where S.Generator.Element == Index>(indices: S) {
indices.sort().lazy.reverse().forEach{ removeAtIndex($0) }
}
}