This should be sufficient for trapping on duplicated indices, because shiftLeft() will be called with the same index twice in a row iff it was included at least twice in the array of indices to remove.
extension Array {
mutating func remove(elementsAtIndices indicesToRemove: [Int]) -> [Element] {
guard !indicesToRemove.isEmpty else {
return []
}
// Copy the removed elements in the specified order.
let removedElements = indicesToRemove.map { self[$0] }
// Sort the indices to remove.
let indicesToRemove = indicesToRemove.sorted()
// Shift the elements we want to keep to the left.
var destIndex = indicesToRemove.first!
var srcIndex = destIndex + 1
var prevRemovedIndex = destIndex
func shiftLeft(untilIndex index: Int) {
precondition(index != prevRemovedIndex)
prevRemovedIndex = index
while srcIndex < index {
self[destIndex] = self[srcIndex]
destIndex += 1
srcIndex += 1
}
srcIndex += 1
}
for removeIndex in indicesToRemove[1...] {
shiftLeft(untilIndex: removeIndex)
}
shiftLeft(untilIndex: self.endIndex)
// Remove the extra elements from the end of the array.
self.removeLast(indicesToRemove.count)
return removedElements
}
}
I’ll try to make it more generic.