Is it possible to override Swift's default array subscripting so that it handles negative indices like Python?
For example, a[-1]
should return the last element of a
, and a[-2]
the element before it.
This should be possible to achieve by extending the Array
type, alas, the code below won't work since it would loop infinitely:
extension Array {
subscript (index:Int) -> [Element] {
return (index < 0) ? self[self.count+index] : self[index]
}
}
How bad would the idea of overriding something that fundamental be?
Actually, there's a relatively good-practice, Swifty way to do this: labelled arguments.
extension CollectionType where Index : BidirectionalIndexType {
subscript(back i: Index.Distance) -> Generator.Element {
return self[endIndex.advancedBy(-i)]
}
}
let ar = [1, 2, 3, 4]
ar[back: 1] // 4
ar[back: 2] // 3
You can obviously change the semantics pretty easily. This implementation, for instance, requires the index be larger than 0. Changing it so 0 returns the last element is as simple as: self[endIndex.predecessor().advancedBy(-i)]
, or, if you want to assume the index is negative going in: self[endIndex.advancedBy(-i)]
.
The advantage of the labelled argument is that it's clear, and no-one would use it by accident.
Swift 4 version:
extension Collection where Index: Comparable {
subscript(back i: Int) -> Iterator.Element {
let backBy = i + 1
return self[self.index(self.endIndex, offsetBy: -backBy)]
}
}
@Hristo, Not only can we use a negative indexing in Swift subscript's functionality but also implement error handling to control whether you're "out of range" or not. So, use the following code for that.
let array: [Int] = [199, 288, 377, 455, 533, 622, 711]
enum SubscriptError: Error {
case greaterThanZero
case lessThanLastIndex
}
extension Collection {
public subscript(negative i: Int) -> () throws -> Element {
let backward = i - 1
if i > 0 {
return { throw SubscriptError.greaterThanZero }
}
if i < -1 * ((endIndex as! Int) - 1) {
print(endIndex)
return { throw SubscriptError.lessThanLastIndex }
}
return { self[index(endIndex, offsetBy: backward)] }
}
}
do {
try array[negative: -6]() // 199
} catch {
print("It's \(error)")
}
The results are as follows:
let array: [Int] = [199, 288, 377, 455, 533, 622, 711]
try array[negative: 2]() // "It's greaterThanZero"
try array[negative: 1]() // "It's greaterThanZero"
try array[negative: 0]() // 711
try array[negative: -1]() // 622
try array[negative: -2]() // 533
try array[negative: -3]() // 455
try array[negative: -4]() // 377
try array[negative: -5]() // 288
try array[negative: -6]() // 199
try array[negative: -7]() // "It's lessThanLastIndex"
try array[negative: -8]() // "It's lessThanLastIndex"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With