Confused
Confused

Reputation: 6278

Swift 3 Array, remove more than one item at once, with .remove(at: i)

Is it possible to remove more than one item from an array, at the same time, using index locations as per .remove(at: i) kind of like:

Pseudo code:

myArray.remove(at: 3, 5, 8, 12)

And if so, what's the syntax for doing this?


UPDATE:

I was trying this, it worked, but the extension in the answer below is much more readable, and sensible, and achieves the goal of one that's exactly as the pseudo code.

an array of "positions" is created: [3, 5, 8, 12]

let sorted = positions.sorted(by: { $1 < $0 })
for index in sorted
{
    myArray.remove(at: index)
}

Upvotes: 22

Views: 24682

Answers (7)

SouthernYankee65
SouthernYankee65

Reputation: 1269

Swift 5.6

I had a situation where I needed to remove values in one list using values from another list, so I used this:

var mainList = [1,2,3,4,5,6,7,8]
let otherList = [3,4,6,8]

mainList.remove(where: { otherList.contains($0) })

print(mainList) // [1, 2, 5, 7]

Upvotes: 0

vadian
vadian

Reputation: 285069

According to the NSMutableArray API I recommend to implement the indexes as IndexSet.

You just need to inverse the order.

extension Array {

    mutating func remove(at indexes: IndexSet) {
        indexes.reversed().forEach{ self.remove(at: $0) }
    }
}

Please see also this answer providing a more efficient algorithm.

Upvotes: 2

linh luu phuoc
linh luu phuoc

Reputation: 71

Swift 4

extension Array {

    mutating func remove(at indexs: [Int]) {
        guard !isEmpty else { return }
        let newIndexs = Set(indexs).sorted(by: >)
        newIndexs.forEach {
            guard $0 < count, $0 >= 0 else { return }
            remove(at: $0)  
        }
    }

}

var arr = ["a", "b", "c", "d", "e", "f"]

arr.remove(at: [2, 3, 1, 4])

result: ["a", "f"]

Upvotes: 7

Kamil Harasimowicz
Kamil Harasimowicz

Reputation: 4984

Simple and clear solution, just Array extension:

extension Array {

    mutating func remove(at indices: [Int]) {
        Set(indices)
            .sorted(by: >)
            .forEach { rmIndex in
                self.remove(at: rmIndex)
            }
    }
}
  • Set(indices) - ensures uniqueness
  • .sorted(by: >) - function removes elements from last to first, so during removal we are sure that indexes are proper

Upvotes: 9

TheTiger
TheTiger

Reputation: 13354

You can make a set of indexes you want to remove.

var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let indexSet = [3, 5, 8, 12]
indexSet.reversed().forEach{ array.remove(at: $0) }
print(array)

Output: [0, 1, 2, 4, 6, 7, 9, 10, 11]

In case indexes are continuous then use removeSubrange

array.removeSubrange(1...3) /// Will remove the elements from 1, 2 and 3 positions.

Upvotes: 4

Krunal
Krunal

Reputation: 79646

Remove elements using indexes of an array elements:

  1. Array of Strings and indexes

    let animals = ["cats", "dogs", "chimps", "moose", "squarrel", "cow"]
    let indexAnimals = [0, 3, 4]
    let arrayRemainingAnimals = animals
        .enumerated()
        .filter { !indexAnimals.contains($0.offset) }
        .map { $0.element }
    
    print(arrayRemainingAnimals)
    
    //result - ["dogs", "chimps", "cow"]
    
  2. Array of Integers and indexes

    var numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
    let indexesToRemove = [3, 5, 8, 12]
    
    numbers = numbers
        .enumerated()
        .filter { !indexesToRemove.contains($0.offset) }
        .map { $0.element }
    
    print(numbers)
    
    //result - [0, 1, 2, 4, 6, 7, 9, 10, 11]
    



Remove elements using element value of another array

  1. Arrays of integers

    var numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
    let elementsTobeRemoved = [3, 5, 8, 12]
    let arrayResult = numbers.filter { element in
        return !elementsTobeRemoved.contains(element)
    }
    print(arrayResult)
    
    //result - [0, 1, 2, 4, 6, 7, 9, 10, 11]
    
  2. Arrays of strings

    let arrayLetters = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
    let arrayRemoveLetters = ["a", "e", "g", "h"]
    let arrayRemainingLetters = arrayLetters.filter {
        !arrayRemoveLetters.contains($0)
    }
    
    print(arrayRemainingLetters)
    
    //result - ["b", "c", "d", "f", "i"]
    

Upvotes: 26

Thanh Pham
Thanh Pham

Reputation: 2090

It's possible if the indexes are continuous using removeSubrange method. For example, if you would like to remove items at index 3 to 5:

myArray.removeSubrange(ClosedRange(uncheckedBounds: (lower: 3, upper: 5)))

For non-continuous indexes, I would recommend remove items with larger index to smaller one. There is no benefit I could think of of removing items "at the same time" in one-liner except the code could be shorter. You can do so with an extension method:

extension Array {
  mutating func remove(at indexes: [Int]) {
    for index in indexes.sorted(by: >) {
      remove(at: index)
    }
  }
}

Then:

myArray.remove(at: [3, 5, 8, 12])

UPDATE: using the solution above, you would need to ensure the indexes array does not contain duplicated indexes. Or you can avoid the duplicates as below:

extension Array {
    mutating func remove(at indexes: [Int]) {
        var lastIndex: Int? = nil
        for index in indexes.sorted(by: >) {
            guard lastIndex != index else {
                continue
            }
            remove(at: index)
            lastIndex = index
        }
    }
}


var myArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
myArray.remove(at: [5, 3, 5, 12]) // duplicated index 5
// result: [0, 1, 2, 4, 6, 7, 8, 9, 10, 11, 13] only 3 elements are removed

Upvotes: 37

Related Questions