Swift Array - 检查索引是否存在

chr*_*ale 124 swift

在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
}
Run Code Online (Sandbox Code Playgroud)

但我明白了

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

Man*_*uel 392

Swift的优雅方式:

let isIndexValid = array.indices.contains(index)
Run Code Online (Sandbox Code Playgroud)

  • 如果你想知道速度差异是什么,我用Xcode的工具测量它,它可以忽略不计.https://gist.github.com/masonmark/a79bfa1204c957043687f4bdaef0c2ad (11认同)
  • 在现实生活中,使用`index <array.count`不是没有关系,但时间复杂度不是更好吗? (8认同)
  • 只是为了说明为什么这是正确的另一点:如果在处理`ArraySlice`时应用相同的逻辑,第一个索引将不是0,所以做`index> = 0`将不够好校验.`.indices`在任何情况下都适用. (5认同)
  • 这是一个很好的答案。为了回答@funct7对时间复杂度的担忧,Array的indices方法返回的类型是一个范围,而不是整数数组。因此,时间复杂度为 O(1),就像手动检查上限和下限时的情况一样。 (3认同)
  • 老实说,因为我从未经历过负索引值的问题,所以您给出的示例有些人为,但是如果确实是这样,那么两次正确/错误检查仍会更好:即index&gt; = 0 &amp;&amp; index &lt;array.count`而不是最坏的情况是n个比较。 (2认同)
  • 我为此爱Swift。这是我的用例:为服务构建糟糕的JSON结构,因此必须这样做:“ attendee3”:names.indices.contains(2)?名称[2]:“” (2认同)

Ben*_*ess 56

Swift 3&4扩展:

extension Collection {

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

}
Run Code Online (Sandbox Code Playgroud)

使用此选项可以在向索引添加关键字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
}
Run Code Online (Sandbox Code Playgroud)

  • 优秀的答案最重要的是它在参数中使用`optional`时是可读的.谢谢! (4认同)
  • 太棒了:D保存了我的一天。 (2认同)
  • 这很漂亮 (2认同)

Ant*_*nio 31

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

if 2 < arr.count {
    ...
} else {
    ...
}
Run Code Online (Sandbox Code Playgroud)

  • 对不起,我没想到那里.早晨咖啡仍然进入血液. (15认同)
  • @NathanMcKaskle数组总是知道它包含多少个元素,因此大小不可能是未知的 (8认同)
  • @NathanMcKaskle无后顾之忧......有时候,即使是几杯咖啡,也会发生在我身上;-) (4认同)
  • 如果数组大小未知怎么办? (3认同)

man*_*mar 12

最好的办法。

  let reqIndex = array.indices.contains(index)
  print(reqIndex)
Run Code Online (Sandbox Code Playgroud)

  • 请在您的答案中添加一些解释,以便其他人可以从中学习 (2认同)

eon*_*ist 11

添加一些延长糖:

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: "c"
//or with guard:
guard let anotherItem = ["a","b","c","d"][safe:3] else {return}
print(anotherItem)//"c"
Run Code Online (Sandbox Code Playgroud)

if let与数组一起进行样式编码时增强了可读性

  • 老实说,这是以最大的可读性和清晰度做到这一点的最快捷方式 (3认同)
  • @barndog 也喜欢它。我通常在我开始的任何项目中将其添加为 Sugar。还添加了守卫示例。感谢 swift slack 社区提出了这个。 (2认同)

das*_*ght 7

你可以用更安全的方式重写这个来检查数组的大小,并使用三元条件:

if let str2 = (arr.count > 2 ? arr[2] : nil) as String?
Run Code Online (Sandbox Code Playgroud)

  • Antonio 的建议更加透明(和高效) 如果您有一个现成的值可以使用并完全消除 if,只留下一个 let 语句,那么三元是合适的。 (2认同)
  • @David Antonio 建议的内容需要两个 `if` 语句,而不是原始代码中的一个 `if` 语句。我的代码用条件运算符替换了第二个“if”,让您保留一个“else”而不是强制使用两个单独的“else”块。 (2认同)
  • 我在他的代码中没有看到两个 if 语句,把 let str2 = arr[1] 放在他的省略号中就完成了。如果您将 OP if let 语句替换为 antonio 的 if 语句,并将赋值移动到内部(或者不移动,因为 str2 的唯一用途是打印它,根本不需要,只需将取消引用内联放在 println 中。更不用说三元运算符只是一个晦涩的(对某些人:))if/else。如果 arr。count &gt; 2,那么 arr[2] 永远不可能为 nil,那为什么要映射到 String 呢?只是这样您就可以应用另一个 if 语句。 (2认同)

Yan*_*eph 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
    }
}
Run Code Online (Sandbox Code Playgroud)

感谢@Benno Kress