Swift Array-インデックスが存在するかどうかを確認します


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
}

しかし、私は得る

致命的なエラー:配列のインデックスが範囲外です

回答:


441

Swiftでのエレガントな方法:

let isIndexValid = array.indices.contains(index)

10
実生活ではそれは問題ではありませんが、時間の複雑さに関しては、使用する方がはるかに良いのではないでしょうindex < array.countか?
funct7

2
私は正直にあなたが与えた例では、私は負のインデックス値を持つ経験したことがありませんので、少し工夫だと思うが、それは本当にケースである必要がある場合は、2真/偽のチェックはまだ良いだろう。つまり、index >= 0 && index < array.count代わりに、最悪のケースであることn個の比較。
funct7

13
速度の違いは何か疑問に思われる場合は、Xcodeのツールを使用して測定しましたが、無視できます。gist.github.com/masonmark/a79bfa1204c957043687f4bdaef0c2ad
メイソン、

7
なぜこれが正しいのかについて別のポイントを追加します。で作業するときに同じロジックを適用するとArraySlice、最初のインデックスは0にindex >= 0ならないため、十分なチェックにはなりません。.indices代わりに、どのような場合でも機能します。
DeFrenZ 2017

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
    }

}

これを使用すると、インデックスにオプションのキーワードを追加したときにオプションの値が返されます。これは、インデックスが範囲外であってもプログラムがクラッシュしないことを意味します。あなたの例では:

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は、パラメーターで使用しているときに読みやすいことです。ありがとう!
ジャクブ2017年

2
素晴らしさ:D私の日を救った。
Codetard 2018

2
これは美しい
JoeGalind

32

インデックスが配列サイズより小さいかどうかを確認してください:

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

3
配列サイズが不明な場合はどうなりますか?
Nathan McKaskle、2015

8
@NathanMcKaskle配列は常にそれが含む要素の数を知っているため、サイズが不明になることはありません
Antonio

16
すみません、そこでは考えていませんでした。朝のコーヒーはまだ血流に入っていました。
Nathan McKaskle、2015

4
@NathanMcKaskle心配する必要はありません...いくつかのコーヒー
Antonio

いくつかの点で、これはO(n)ではなくO(1)で実行されるため、最良の答えです。
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
正直なところ、これは最大の読みやすさと明快さでそれを行う最も迅速な方法です
バーンドッグ

1
@barndog大好きです。私は通常、これを開始するプロジェクトのSugarとして追加します。ガードの例も追加しました。このコミュニティを思いついたのはSwift Slackコミュニティの功績です。
eonist

良い解決策...しかし、あなたが与えている例では出力は「d」を出力します...
jayant rawat

これはSwift言語の一部である必要があります。範囲外のインデックスの問題は、nil値と同じくらい問題です。同様の構文を使用することをお勧めします。たとえば、myArray [?3]のようになります。myArrayがオプションの場合、myArray?[?3]を取得します
Andy Weinstein

@Andyワインスタインは、あなたは💪りんごにそれを提案しなければならない
eonist

7

これをより安全な方法で書き直して配列のサイズをチェックし、3項条件式を使用できます。

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

1
Antonioが提案するものははるかに透明(かつ効率的)です。3項が適切なのは、すぐに使用できる値があり、ifを完全に削除して、letステートメントを残す場合です。
David Berry

1
@David Antonioの提案では、元のコードのif1つのifステートメントではなく2つのステートメントが必要です。私のコードでは、2つ目ifを条件演算子に置き換えて、2 elseつの別々のelseブロックを強制する代わりに、1つを保持できるようにしています。
dasblinkenlight 2014

1
彼のコードに2つのifステートメントが表示されていないので、省略記号にlet str2 = arr [1]を入力すれば完了です。OP if letステートメントをアントニオのifステートメントで置き換え、内部に割り当てを移動する場合(またはそうでない場合、str2の唯一の使用法はそれを印刷することなので、まったく必要がないので、delnlineをprintlnにインラインで配置するだけです。三項演算子は(一部の人にとっては)あいまいなif / elseです。到着した場合。count> 2の場合、arr [2]がnilになることはないため、なぜStringにマッピングするのでしょうか。そのため、さらに別のifステートメントを適用できます。
David Berry

@David ifOPからの質問全体は、Antonioの回答の「then」ブランチ内に収まるため、2つの入れ子になりますif。私はOPsコードを小さな例として表示しているので、彼はまだを望んでいると思いますif。彼の例ifでは不要であることに同意します。しかし、その後、再びOPが配列の十分な長さを持っていないことを知っているので、文全体は、無意味であり、その要素のどれもありませんnil、彼は削除することもできますので、ifとだけその維持elseのブロックを。
dasblinkenlight 2014

いいえ、そうではありません。AntonioのifがOPのifステートメントを置き換えます。配列型は[String]であるため、nilを含むことはできないため、長さを超えるものをチェックする必要はありません。
David Berry

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を処理するのは私にとって仕事です。


インデックスが負の場合はどうなりますか?あなたもおそらくそれをチェックする必要があります。
Frankie Simon
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.