Swift Array instance method drop(at: Int)

2019-08-23 12:14发布

问题:

Array in Swift has several instance methods for excluding elements, such as dropFirst(), dropLast(), drop(while:), etc. What about drop(at:)?

Note: I'd use remove(at:), but the array I'm working with is a let constant.

回答1:

Note: I'd use remove(at:), but the array I'm working with is a constant.

I wouldn't let that stop you:

extension Array {
    func drop(at:Int) -> [Element] {
        var temp = self
        temp.remove(at: at)
        return temp
    }
}
let numbers = [1, 2, 3, 4, 5]
print(numbers.drop(at: 2)) // [1, 2, 4, 5]


回答2:

You can extend RangeReplaceableCollection protocol instead of Array type, this way you can use it on Strings as well:

extension RangeReplaceableCollection {
    func drop(at offset: Int) -> SubSequence {
        let index = self.index(startIndex, offsetBy: offset, limitedBy: endIndex) ?? endIndex
        let next = self.index(index, offsetBy: 1, limitedBy: endIndex) ?? endIndex
        return self[..<index] + self[next...]
    }
}

var str = "Hello, playground"
str.drop(at: 5)  // "Hello playground"

let numbers = [1, 2, 3, 4, 5]
print(numbers.drop(at: 2))  // "[1, 2, 4, 5]\n"

If you would like to accept also String.Index in your method:

extension RangeReplaceableCollection {
    func drop(at index: Index) -> SubSequence {
        let index = self.index(startIndex, offsetBy: distance(from: startIndex, to: index), limitedBy: endIndex) ?? endIndex
        let next = self.index(index, offsetBy: 1, limitedBy: endIndex) ?? endIndex
        return self[..<index] + self[next...]
    }
}

var str = "Hello, playground"
str.drop(at: 0)               // "ello, playground"
str.drop(at: str.startIndex)  // "ello, playground"


回答3:

The only thing I would add to your implementation is a guard statement with a useful error message:

extension Array {
    func drop(at index: Int) -> ArraySlice<Element> {
        guard indices.contains(index) else {
            fatalError("Index out of range")
        }
        return self[0..<index] + self[(index+1)..<endIndex]
    }

    func drop(range: CountableRange<Int>) -> ArraySlice<Element> {
        guard (indices.lowerBound, range.upperBound) <= (range.lowerBound, indices.upperBound) else {
            fatalError("Range is out of the indices bounds")
        }
        return self[0..<range.lowerBound] + self[range.upperBound..<endIndex]
    }
}

let a = [1,2,3]
a.drop(at: 3)          //Fatal error: Index out of range
let b = [0,1,2,3,4,5]
b.drop(range: 1..<5)   //[0, 5]


回答4:

return self[0..<index] + self[index+1..<endIndex]

Ugly. Why not use the tools you're given?

extension Array {
    func drop(at:Int) -> ArraySlice<Element> {
        return prefix(upTo: at) + suffix(from: at+1)
    }
}
let arr = [1,2,3,4,5]
let slice = arr.drop(at:2) // [1,2,4,5]

EDIT It seems Apple would prefer you to say (using partial ranges)

return self[..<at] + self[(at+1)...]

but personally I think that's uglier, and after all the methods I suggested are not deprecated or anything.



回答5:

How about using a Swift extension to add drop(at:) to the Array structure?

extension Array {
  func drop(at index: Int) -> ArraySlice<Element> {
    precondition(indices.contains(index), "Index out of range")
    return self[..<index] + self[(index+1)...]
  }
}

It returns a slice of the original array without the element at the specified index.

let numbers = [1, 2, 3, 4, 5]
print(numbers.drop(at: 2))
// Prints "[1, 2, 4, 5]"

Note: You may also want to add drop(at:) to ArraySlice.