Array Cepat - Periksa apakah ada indeks

139

Di Swift, apakah ada cara untuk memeriksa apakah indeks ada dalam array tanpa kesalahan fatal yang dilemparkan?

Saya berharap bisa melakukan sesuatu seperti ini:

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
}

Tapi saya mengerti

fatal fatal: Array index diluar jangkauan

Chrishale
sumber

Jawaban:

441

Cara yang elegan di Swift:

let isIndexValid = array.indices.contains(index)
Manuel
sumber
10
Dalam kehidupan nyata, itu seharusnya tidak masalah tetapi dari segi kompleksitas waktu bukankah itu akan jauh lebih baik untuk digunakan index < array.count?
funct7
2
Jujur saya pikir contoh yang Anda berikan agak dibuat-buat karena saya tidak pernah mengalami memiliki nilai indeks negatif, tetapi jika itu benar-benar terjadi, dua cek benar / salah akan tetap lebih baik: yaitu index >= 0 && index < array.countalih-alih kasus terburuk menjadi n perbandingan.
funct7
13
Jika Anda bertanya-tanya apa perbedaan kecepatannya, saya mengukurnya dengan alat Xcode dan dapat diabaikan. gist.github.com/masonmark/a79bfa1204c957043687f4bdaef0c2ad
Mason
7
Hanya untuk menambahkan poin lain tentang mengapa ini benar: jika Anda menerapkan logika yang sama ketika bekerja pada ArraySlice, indeks pertama tidak akan menjadi 0, jadi melakukan index >= 0tidak akan menjadi pemeriksaan yang cukup baik. .indicessebaliknya berfungsi dalam hal apa pun.
DeFrenZ
2
Saya suka Swift untuk ini. Inilah kasus penggunaan saya: membangun struktur JSON yang menyebalkan untuk layanan, jadi harus melakukan ini: "attendee3": names.indices.contains (2)? nama [2]: ""
Lee Probert
64

Jenis ekstensi:

extension Collection {

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

}

Dengan ini, Anda mendapatkan nilai opsional kembali ketika menambahkan kata kunci opsional ke indeks Anda yang berarti program Anda tidak crash bahkan jika indeks berada di luar jangkauan. Dalam contoh Anda:

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
}
Benno Kress
sumber
4
Jawaban luar biasa 👏 Paling penting itu dapat dibaca saat menggunakan optionaldalam parameter. Terima kasih!
Jakub
2
Kedahsyatan: D Menyelamatkan hari saya.
Codetard
2
Ini indah
JoeGalind
32

Periksa apakah indeks kurang dari ukuran array:

if 2 < arr.count {
    ...
} else {
    ...
}
Antonio
sumber
3
Bagaimana jika ukuran array tidak diketahui?
Nathan McKaskle
8
@NathanMcKaskle Array selalu tahu berapa banyak elemen yang dikandungnya, jadi ukurannya tidak bisa diketahui
Antonio
16
Maaf saya tidak berpikir di sana. Kopi pagi masih memasuki aliran darah.
Nathan McKaskle
4
@NathanMcKaskle jangan khawatir ... kadang-kadang itu terjadi pada saya, bahkan setelah beberapa kopi ;-)
Antonio
Dalam beberapa hal, ini adalah jawaban terbaik, karena ini berjalan dalam O (1), bukan O (n).
ScottyBlades
12

Tambahkan sedikit gula tambahan:

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"

Meningkatkan keterbacaan saat melakukan if letpengkodean gaya bersama dengan array

eonist
sumber
2
jujur, ini adalah cara paling cepat untuk melakukannya dengan keterbacaan maksimum dan kejelasan
barndog
1
@ Barndog Senang juga. Saya biasanya menambahkan ini sebagai Gula dalam proyek apa pun saya Mulai. Menambahkan contoh penjaga juga. Penghargaan untuk komunitas slack slack untuk membuat yang ini.
eonist
solusi yang bagus ... tetapi Output akan mencetak "d" sebagai contoh Anda memberikan ...
jayant rawat
Ini harus menjadi bagian dari bahasa Swift. Masalah indeks di luar kisaran tidak kurang bermasalah dari nilai nol. Saya bahkan menyarankan menggunakan sintaksis yang serupa: mungkin sesuatu seperti: myArray [? 3]. Jika myArray adalah opsional, Anda akan mendapatkan myArray? [? 3]
Andy Weinstein
@Andy Weinstein Anda harus melamarnya ke apel 💪
eonist
7

Anda dapat menulis ulang ini dengan cara yang lebih aman untuk memeriksa ukuran array, dan menggunakan kondisional ternary:

if let str2 = (arr.count > 2 ? arr[2] : nil) as String?
dasblinkenlight
sumber
1
Apa yang disarankan Antonio jauh lebih transparan (dan efisien) Di mana terner akan sesuai adalah jika Anda memiliki nilai yang tersedia untuk digunakan dan menghilangkan jika sama sekali, hanya meninggalkan pernyataan let.
David Berry
1
@ David Menunjukkan Apa yang disarankan Antonio membutuhkan dua ifpernyataan alih-alih satu ifpernyataan dalam kode asli. Kode saya menggantikan yang kedua ifdengan operator bersyarat, membiarkan Anda menyimpan satu elsealih-alih memaksa dua elseblok terpisah .
dasblinkenlight
1
Saya tidak melihat dua pernyataan if dalam kodenya, masukkan str2 = arr [1] untuk ellipsisnya dan selesai. Jika Anda mengganti OP jika membiarkan pernyataan dengan pernyataan jika antonio, dan memindahkan penugasan ke dalam (atau tidak, karena satu-satunya penggunaan str2 adalah untuk mencetaknya, tidak perlu sama sekali, cukup masukkan dereference inline di println. Belum lagi bahwa operator ternary hanyalah kabur (untuk beberapa :)) jika / selain itu. Jika arr. hitung> 2, lalu arr [2] tidak pernah bisa nol, jadi mengapa memetakannya ke String? supaya Anda bisa menerapkan pernyataan if lagi.
David Berry
@ David Seluruh ifdari pertanyaan OP akan berakhir di dalam "kemudian" cabang jawaban Antonio, jadi akan ada dua bersarang if. Saya melihat kode OP sebagai contoh kecil, jadi saya berasumsi bahwa dia masih menginginkan if. Saya setuju dengan Anda bahwa dalam contohnya iftidak perlu. Tetapi sekali lagi, seluruh pernyataan tidak ada gunanya, karena OP tahu bahwa array tidak memiliki panjang yang cukup, dan tidak ada elemen yang ada nil, sehingga ia dapat menghapus ifdan hanya menyimpan elsebloknya.
dasblinkenlight
Tidak, itu tidak akan terjadi. Antonio's if menggantikan pernyataan OP's if. Karena tipe arraynya adalah [String] Anda tahu bahwa itu tidak akan pernah mengandung nol, jadi tidak perlu memeriksa apa pun di luar panjangnya.
David Berry
7

Ekstensi Swift 4:

Bagi saya saya lebih suka metode seperti.

// 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
    }
}

Terima kasih kepada @ Benno Kress

YannSteph
sumber
1

Menegaskan jika ada indeks array:

Metodologi ini bagus jika Anda tidak ingin menambahkan gula tambahan:

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
eonist
sumber
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
    }

}

Ini berfungsi bagi saya untuk menangani indexOutOfBounds .

Pratik Sodha
sumber
Bagaimana jika indeksnya negatif? Anda mungkin harus memeriksa itu juga.
Frankie Simon