Swift数组-检查索引是否存在


139

在Swift中,是否有任何方法可以检查数组中是否存在索引而不会引发致命错误?

我希望我可以做这样的事情:

let arr: [String] = ["foo", "bar"]
let str: String? = arr[1]
if let str2 = arr[2] as String? {
    // this wouldn't run
    println(str2)
} else {
    // this would be run
}

但是我明白了

致命错误:数组索引超出范围

Answers:


441

Swift中的一种优雅方式:

let isIndexValid = array.indices.contains(index)

10
在现实生活中,这并不重要,但是在时间复杂度方面,使用它会更好index < array.count吗?
funct7'9

2
老实说,由于我从未经历过负索引值的问题,因此您给出的示例有些人为,但是如果确实如此,则两次正确/错误检查仍会更好:即,index >= 0 && index < array.count不是最坏的情况是n比较。
funct7'9

13
如果您想知道速度差异是多少,我可以使用Xcode的工具对其进行测量,并且可以忽略不计。gist.github.com/masonmark/a79bfa1204c957043687f4bdaef0c2ad
Mason

7
只是要补充一下这是正确的原因:如果在处理时应用相同的逻辑ArraySlice,则第一个索引将不会为0,因此这样做index >= 0将不是一个足够好的检查。.indices而是在任何情况下都有效。
DeFrenZ

2
我为此爱Swift。这是我的用例:为服务构建糟糕的JSON结构,因此必须这样做:“ attendee3”:names.indices.contains(2)?names [2]:“”
Lee Probert

64

类型扩展名:

extension Collection {

    subscript(optional i: Index) -> Iterator.Element? {
        return self.indices.contains(i) ? self[i] : nil
    }

}

使用此方法,当在索引中添加关键字optional时,您将获得一个可选值,这意味着即使索引超出范围,程序也不会崩溃。在您的示例中:

let arr = ["foo", "bar"]
let str1 = arr[optional: 1] // --> str1 is now Optional("bar")
if let str2 = arr[optional: 2] {
    print(str2) // --> this still wouldn't run
} else {
    print("No string found at that index") // --> this would be printed
}

4
出色的回答👏最重要的是,optional在参数中使用时可读。谢谢!
雅各布

2
太棒了:D保存了我的一天。
Codetard

2
这很漂亮
JoeGalind

32

只需检查索引是否小于数组大小即可:

if 2 < arr.count {
    ...
} else {
    ...
}

3
如果数组大小未知,该怎么办?
内森·麦卡斯凯尔

8
@NathanMcKaskle数组始终知道它包含多少个元素,因此大小不能未知
Antonio

16
抱歉,我当时没想。早晨咖啡仍在进入血液。
内森·麦卡斯凯尔

4
@NathanMcKaskle不用担心...即使在喝了几杯咖啡之后,有时也会发生在我身上;-)
Antonio

在某些方面,这是最佳答案,因为它在O(1)而不是O(n)中运行。
ScottyBlades

12

添加一些扩展糖:

extension Collection {
  subscript(safe index: Index) -> Iterator.Element? {
    guard indices.contains(index) else { return nil }
    return self[index]
  }
}

if let item = ["a", "b", "c", "d"][safe: 3] { print(item) }//Output: "d"
//or with guard:
guard let anotherItem = ["a", "b", "c", "d"][safe: 3] else {return}
print(anotherItem) // "d"

if let与数组结合进行样式编码时,增强了可读性


2
老实说,这是最快的方式来实现最大程度的可读性和清晰度
barndog

1
@barndog也喜欢它。我通常在我开始的任何项目中将其添加为Sugar。还添加了后卫示例。感谢迅速懒散的社区提出这一建议。
eonist

好的解决方案...但是在您给出的示例中,输出将显示“ d”
jayant rawat

这应该是Swift语言的一部分。索引超出范围的问题不少于nil值。我什至建议使用类似的语法:也许像:myArray [?3]。如果myArray是可选的,您将得到myArray?[?3]
Andy Weinstein

@Andy韦恩斯坦你应该把它提出来苹果💪
eonist

7

您可以用一种更安全的方式重写它来检查数组的大小,并使用三元条件:

if let str2 = (arr.count > 2 ? arr[2] : nil) as String?

1
Antonio提出的建议更加透明(高效)在适合使用三元数据的情况下,如果您具有随时可用的值并完全消除了if,则只留下let语句。
David Berry

1
@David Antonio的建议需要两个if语句,而不是if原始代码中的一个语句。我的代码用if条件运算符替换了第二个,让您保持单个else而不是强制两个单独的else块。
dasblinkenlight 2014年

1
我没有在他的代码中看到两个if语句,将let str2 = arr [1]放入了他的省略号就完成了。如果您用antonio的if语句替换OP if let语句,然后将赋值移到内部(或者不这样做,因为str2的唯一用法就是打印它,那么根本就不需要,只需将取消引用内联放在println中即可。如果/否则,三元运算符只是一个晦涩的(对某些人而言:))。如果是。count> 2,则arr [2]永远不能为nil,那么为什么将其映射到String?只是这样您可以应用另一个if语句。
David Berry

@David ifOP的问题的全部内容都将落入Antonio的答案的“ then”分支内,因此将有两个嵌套的ifs。我正在查看OP代码作为一个小示例,因此我假设他仍然想要一个if。我同意你的看法,在他的例子if中没有必要。但是同样,整个语句毫无意义,因为OP知道数组没有足够的长度,并且数组的任何元素都不是nil,因此他可以删除if并仅保留其else块。
dasblinkenlight 2014年

不,不会。Antonio的if替换了OP的if语句。因为数组类型是[String],所以您知道它永远不能包含nil,因此不需要检查超出长度的任何内容。
David Berry 2014年

7

Swift 4扩展名:

对我来说,我更喜欢方法。

// MARK: - Extension Collection

extension Collection {

    /// Get at index object
    ///
    /// - Parameter index: Index of object
    /// - Returns: Element at index or nil
    func get(at index: Index) -> Iterator.Element? {
        return self.indices.contains(index) ? self[index] : nil
    }
}

感谢@Benno Kress


1

声明是否存在数组索引:

如果您不想添加扩展糖,则此方法非常有用:

let arr = [1,2,3]
if let fourthItem = (3 < arr.count ?  arr[3] : nil ) {
     Swift.print("fourthItem:  \(fourthItem)")
}else if let thirdItem = (2 < arr.count ?  arr[2] : nil) {
     Swift.print("thirdItem:  \(thirdItem)")
}
//Output: thirdItem: 3

1
extension Array {
    func isValidIndex(_ index : Int) -> Bool {
        return index < self.count
    }
}

let array = ["a","b","c","d"]

func testArrayIndex(_ index : Int) {

    guard array.isValidIndex(index) else {
        print("Handle array index Out of bounds here")
        return
    }

}

对我来说,处理indexOutOfBounds是工作。


如果指数为负怎么办?您可能还应该检查一下。
弗兰基·西蒙
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.