Aman pencarian array (diperiksa batas) di Swift, melalui binding opsional?

271

Jika saya memiliki array di Swift, dan mencoba mengakses indeks yang di luar batas, ada kesalahan runtime yang tidak mengejutkan:

var str = ["Apple", "Banana", "Coconut"]

str[0] // "Apple"
str[3] // EXC_BAD_INSTRUCTION

Namun, saya akan berpikir dengan semua perangkaian opsional dan keamanan yang dibawa Swift, akan sepele untuk melakukan sesuatu seperti:

let theIndex = 3
if let nonexistent = str[theIndex] { // Bounds check + Lookup
    print(nonexistent)
    ...do other things with nonexistent...
}

Dari pada:

let theIndex = 3
if (theIndex < str.count) {         // Bounds check
    let nonexistent = str[theIndex] // Lookup
    print(nonexistent)   
    ...do other things with nonexistent... 
}

Tapi ini bukan masalahnya - saya harus menggunakan pernyataan ol ' ifuntuk memeriksa dan memastikan indeks kurang dari str.count.

Saya mencoba menambahkan subscript()implementasi saya sendiri , tetapi saya tidak yakin bagaimana meneruskan panggilan ke implementasi asli, atau mengakses item (berbasis indeks) tanpa menggunakan notasi subskrip:

extension Array {
    subscript(var index: Int) -> AnyObject? {
        if index >= self.count {
            NSLog("Womp!")
            return nil
        }
        return ... // What?
    }
}
Craig Otis
sumber
2
Saya menyadari ini sedikit OT, tetapi saya juga merasa akan lebih baik jika Swift memiliki sintaks yang jelas untuk melakukan pemeriksaan batas, termasuk daftar. Kami sudah memiliki kata kunci yang cocok untuk ini, masuk. Jadi misalnya, jika X in (1,2,7) ... atau jika X di myArray
Maury Markowitz

Jawaban:

652

Jawaban Alex memiliki saran dan solusi yang bagus untuk pertanyaan itu, namun, saya kebetulan menemukan cara yang lebih baik dalam mengimplementasikan fungsi ini:

Swift 3.2 dan yang lebih baru

extension Collection {

    /// Returns the element at the specified index if it is within bounds, otherwise nil.
    subscript (safe index: Index) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

Swift 3.0 dan 3.1

extension Collection where Indices.Iterator.Element == Index {

    /// Returns the element at the specified index if it is within bounds, otherwise nil.
    subscript (safe index: Index) -> Generator.Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

Penghargaan untuk Hamish karena menghasilkan solusi untuk Swift 3 .

Cepat 2

extension CollectionType {

    /// Returns the element at the specified index if it is within bounds, otherwise nil.
    subscript (safe index: Index) -> Generator.Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

Contoh

let array = [1, 2, 3]

for index in -20...20 {
    if let item = array[safe: index] {
        print(item)
    }
}
Nikita Kukushkin
sumber
45
Saya pikir ini pasti patut mendapat perhatian - kerja bagus. Saya suka safe:nama parameter yang disertakan untuk memastikan perbedaannya.
Craig Otis
11
Pada Swift 2 (Xcode 7) ini perlu sedikit tweak:return self.indices ~= index ? self[index] : nil;
Tim
7
Mengenai versi Swift 3: mungkin prompt corner-case-only-prompt, namun prompt: ada beberapa kasus di mana versi subskrip "aman" di atas tidak aman (sedangkan versi Swift 2 adalah): untuk Collectionjenis di mana Indicesada tidak berdekatan. Misalnya untuk Setcontoh, jika kita mengakses elemen set dengan indeks ( SetIndex<Element>), kita dapat mengalami pengecualian runtime untuk indeks yang ada >= startIndexdan < endIndex, dalam hal ini subskrip aman gagal (lihat misalnya contoh yang dibuat ini ).
dfri
12
PERINGATAN! Memeriksa array dengan cara ini bisa sangat mahal. The containsMetode akan iterate melalui semua indeks sehingga membuat O ini (n). Cara yang lebih baik adalah dengan menggunakan indeks dan menghitung untuk memeriksa batas.
Stefan Vasiljevic
6
Untuk mencegah pembuatan indeks dan mengulanginya (O (n)), lebih baik menggunakan perbandingan (O (1)): return index >= startIndex && index < endIndex ? self[index] : nil Collectiontipe miliki startIndex, endIndexyang mana Comparable. Tentu saja, ini tidak akan berfungsi untuk beberapa koleksi aneh yang, misalnya, tidak memiliki indeks di tengah, solusi dengan indiceslebih umum.
zubko
57

Jika Anda benar-benar menginginkan perilaku ini, baunya seperti Anda menginginkan Kamus alih-alih Array. Kamus kembali nilketika mengakses kunci yang hilang, yang masuk akal karena jauh lebih sulit untuk mengetahui apakah kunci hadir dalam kamus karena kunci itu bisa apa saja, di mana dalam array kunci harus dalam kisaran: 0untuk count. Dan itu sangat umum untuk beralih pada rentang ini, di mana Anda dapat benar - benar yakin memiliki nilai nyata pada setiap iterasi dari sebuah loop.

Saya pikir alasannya tidak bekerja seperti ini adalah pilihan desain yang dibuat oleh pengembang Swift. Ambil contoh Anda:

var fruits: [String] = ["Apple", "Banana", "Coconut"]
var str: String = "I ate a \( fruits[0] )"

Jika Anda sudah tahu indeks ada, seperti yang Anda lakukan dalam kebanyakan kasus di mana Anda menggunakan array, kode ini hebat. Namun, jika mengakses subscript mungkin bisa kembali nilmaka Anda telah mengubah jenis kembali dari Array's subscriptmetode untuk menjadi opsional. Ini mengubah kode Anda menjadi:

var fruits: [String] = ["Apple", "Banana", "Coconut"]
var str: String = "I ate a \( fruits[0]! )"
//                                     ^ Added

Yang berarti Anda harus membuka bungkusan opsional setiap kali Anda mengulangi melalui array, atau melakukan hal lain dengan indeks yang diketahui, hanya karena jarang Anda dapat mengakses indeks di luar batas. Perancang Swift memilih untuk lebih sedikit membuka bungkus opsional, dengan mengorbankan pengecualian runtime saat mengakses indeks di luar batas. Dan kerusakan lebih baik daripada kesalahan logika yang disebabkan oleh nilAnda tidak mengharapkan data Anda di suatu tempat.

Dan saya setuju dengan mereka. Jadi Anda tidak akan mengubah Arrayimplementasi default karena Anda akan merusak semua kode yang mengharapkan nilai-nilai non-opsional dari array.

Sebagai gantinya, Anda bisa mensubklasifikasikan Array, dan mengganti subscriptuntuk mengembalikan opsional. Atau, lebih praktisnya, Anda bisa memperluas Arraydengan metode non-subskrip yang melakukan ini.

extension Array {

    // Safely lookup an index that might be out of bounds,
    // returning nil if it does not exist
    func get(index: Int) -> T? {
        if 0 <= index && index < count {
            return self[index]
        } else {
            return nil
        }
    }
}

var fruits: [String] = ["Apple", "Banana", "Coconut"]
if let fruit = fruits.get(1) {
    print("I ate a \( fruit )")
    // I ate a Banana
}

if let fruit = fruits.get(3) {
    print("I ate a \( fruit )")
    // never runs, get returned nil
}

Pembaruan Swift 3

func get(index: Int) ->T? perlu diganti oleh func get(index: Int) ->Element?

Alex Wayne
sumber
2
+1 (dan menerima) untuk menyebutkan masalah dengan mengubah jenis kembali subscript()menjadi opsional - ini adalah hambatan utama yang dihadapi dalam mengesampingkan perilaku default. (Saya benar-benar tidak bisa membuatnya bekerja sama sekali. ) Saya menghindari menulis get()metode ekstensi, yang merupakan pilihan yang jelas dalam skenario lain (Obj-C kategori, siapa pun?) Tetapi get(tidak jauh lebih besar dari [, dan membuatnya jelas bahwa perilakunya mungkin berbeda dari apa yang pengembang lain harapkan dari operator subskrip Swift. Terima kasih!
Craig Otis
3
Untuk membuatnya lebih pendek, saya gunakan di ();) Terima kasih!
hyouuu
7
Pada Swift 2.0 Ttelah diubah namanya menjadi Element. Hanya pengingat ramah :)
Stas Zhukovskiy
3
Untuk menambah diskusi ini, alasan lain mengapa memeriksa batas tidak dimasukkan ke Swift untuk mengembalikan opsional adalah karena kembali nilbukannya menyebabkan pengecualian dari indeks di luar batas akan menjadi ambigu. Karena eg Array<String?>dapat juga mengembalikan nil sebagai anggota koleksi yang valid, Anda tidak akan dapat membedakan antara kedua kasus tersebut. Jika Anda memiliki jenis koleksi Anda sendiri yang Anda tahu tidak pernah dapat mengembalikan nilnilai, alias itu kontekstual ke aplikasi, maka Anda dapat memperpanjang Swift untuk memeriksa batas aman seperti dijawab dalam posting ini.
Aaron
Bekerja dengan indah
kamyFC
20

Untuk membangun berdasarkan jawaban Nikita Kukushkin, kadang-kadang Anda perlu menetapkan dengan aman ke indeks array serta membaca dari mereka, yaitu

myArray[safe: badIndex] = newValue

Jadi di sini adalah pembaruan untuk jawaban Nikita (Swift 3.2) yang juga memungkinkan penulisan dengan aman ke indeks array yang dapat diubah, dengan menambahkan safe: nama parameter.

extension Collection {
    /// Returns the element at the specified index iff it is within bounds, otherwise nil.
    subscript(safe index: Index) -> Element? {
        return indices.contains(index) ? self[ index] : nil
    }
}

extension MutableCollection {
    subscript(safe index: Index) -> Element? {
        get {
            return indices.contains(index) ? self[ index] : nil
        }

        set(newValue) {
            if let newValue = newValue, indices.contains(index) {
                self[ index] = newValue
            }
        }
    }
}
SafeFastExpressive
sumber
2
Jawaban yang sangat diremehkan! Ini adalah cara yang benar untuk melakukan ini!
Reid
14

Berlaku dalam Swift 2

Meskipun ini sudah dijawab berkali-kali, saya ingin memberikan jawaban yang lebih sesuai di mana cara pemrograman Swift, yang dalam kata-kata Crusty adalah: "Pikirkan protocoldulu"

• Apa yang ingin kita lakukan?
- Dapatkan Elemen dari ArrayIndeks yang diberikan hanya ketika aman, dan niljika tidak
• Apa yang menjadi dasar penerapan fungsi ini?
- Array subscripting
• Dari mana ia mendapatkan fitur ini?
- Definisi struct Arraydalam Swiftmodul memilikinya
• Tidak ada yang lebih generik / abstrak?
- Mengadopsi protocol CollectionTypeyang memastikan juga
• Tidak ada yang lebih generik / abstrak?
- Ini juga mengadopsi protocol Indexable...
• Ya, sepertinya yang terbaik yang bisa kita lakukan. Bisakah kita mengembangkannya untuk memiliki fitur yang kita inginkan?
- Tapi kami memiliki tipe yang sangat terbatas (tidak Int) dan properti (tidakcount) untuk bekerja dengan sekarang!
• Itu sudah cukup. Stdlib Swift dilakukan dengan cukup baik;)

extension Indexable {
    public subscript(safe safeIndex: Index) -> _Element? {
        return safeIndex.distanceTo(endIndex) > 0 ? self[safeIndex] : nil
    }
}

¹: tidak benar, tetapi memberikan ide

DeFrenZ
sumber
2
Sebagai seorang pemula Swift, saya tidak mengerti jawaban ini. Apa yang dilambangkan oleh kode pada bagian akhir? Apakah itu solusi, dan jika demikian, bagaimana cara saya menggunakannya?
Thomas Tempelmann
3
Maaf, jawaban ini tidak berlaku lagi untuk Swift 3, tetapi prosesnya tentu saja. Satu-satunya perbedaan adalah bahwa sekarang Anda harus berhenti di Collectionmungkin :)
DeFrenZ
11
extension Array {
    subscript (safe index: Index) -> Element? {
        return 0 <= index && index < count ? self[index] : nil
    }
}
  • O (1) kinerja
  • ketik aman
  • dengan benar berurusan dengan Opsional untuk [MyType?] (mengembalikan MyType ??, yang dapat dibuka pada kedua level)
  • tidak menyebabkan masalah untuk Set
  • kode ringkas

Berikut ini beberapa tes yang saya lakukan untuk Anda:

let itms: [Int?] = [0, nil]
let a = itms[safe: 0] // 0 : Int??
a ?? 5 // 0 : Int?
let b = itms[safe: 1] // nil : Int??
b ?? 5 // nil : Int?
let c = itms[safe: 2] // nil : Int??
c ?? 5 // 5 : Int?
terima kasih
sumber
10
  • Karena array dapat menyimpan nilai nil, tidak masuk akal untuk mengembalikan nol jika array [indeks] panggilan di luar batas.
  • Karena kami tidak tahu bagaimana pengguna ingin mengatasi masalah batas, tidak masuk akal untuk menggunakan operator khusus.
  • Sebaliknya, gunakan aliran kontrol tradisional untuk membuka objek dan memastikan keamanan jenis.

jika biarkan index = array.checkIndexForSafety (index: Int)

  let item = array[safeIndex: index] 

jika biarkan index = array.checkIndexForSafety (index: Int)

  array[safeIndex: safeIndex] = myObject
extension Array {

    @warn_unused_result public func checkIndexForSafety(index: Int) -> SafeIndex? {

        if indices.contains(index) {

            // wrap index number in object, so can ensure type safety
            return SafeIndex(indexNumber: index)

        } else {
            return nil
        }
    }

    subscript(index:SafeIndex) -> Element {

        get {
            return self[index.indexNumber]
        }

        set {
            self[index.indexNumber] = newValue
        }
    }

    // second version of same subscript, but with different method signature, allowing user to highlight using safe index
    subscript(safeIndex index:SafeIndex) -> Element {

        get {
            return self[index.indexNumber]
        }

        set {
            self[index.indexNumber] = newValue
        }
    }

}

public class SafeIndex {

    var indexNumber:Int

    init(indexNumber:Int){
        self.indexNumber = indexNumber
    }
}
MS Cline
sumber
1
Pendekatan yang menarik. Ada alasan mengapa SafeIndexkelas dan bukan struct?
stef
8

Cepat 4

Ekstensi untuk mereka yang lebih memilih sintaksis yang lebih tradisional:

extension Array {

    func item(at index: Int) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }
}
Matjan
sumber
Anda tidak perlu membatasi elemen array agar setara untuk memeriksa apakah indeks berisi indeksnya.
Leo Dabus
ya - poin bagus - itu hanya akan diperlukan untuk metode aman tambahan seperti deleteObject, dll.
Matjan
5

Saya menemukan array aman dapatkan, atur, masukkan, hapus sangat berguna. Saya lebih suka masuk dan mengabaikan kesalahan karena semua yang lain segera menjadi sulit untuk dikelola. Kode lengkap di bawah

/**
 Safe array get, set, insert and delete.
 All action that would cause an error are ignored.
 */
extension Array {

    /**
     Removes element at index.
     Action that would cause an error are ignored.
     */
    mutating func remove(safeAt index: Index) {
        guard index >= 0 && index < count else {
            print("Index out of bounds while deleting item at index \(index) in \(self). This action is ignored.")
            return
        }

        remove(at: index)
    }

    /**
     Inserts element at index.
     Action that would cause an error are ignored.
     */
    mutating func insert(_ element: Element, safeAt index: Index) {
        guard index >= 0 && index <= count else {
            print("Index out of bounds while inserting item at index \(index) in \(self). This action is ignored")
            return
        }

        insert(element, at: index)
    }

    /**
     Safe get set subscript.
     Action that would cause an error are ignored.
     */
    subscript (safe index: Index) -> Element? {
        get {
            return indices.contains(index) ? self[index] : nil
        }
        set {
            remove(safeAt: index)

            if let element = newValue {
                insert(element, safeAt: index)
            }
        }
    }
}

Tes

import XCTest

class SafeArrayTest: XCTestCase {
    func testRemove_Successful() {
        var array = [1, 2, 3]

        array.remove(safeAt: 1)

        XCTAssert(array == [1, 3])
    }

    func testRemove_Failure() {
        var array = [1, 2, 3]

        array.remove(safeAt: 3)

        XCTAssert(array == [1, 2, 3])
    }

    func testInsert_Successful() {
        var array = [1, 2, 3]

        array.insert(4, safeAt: 1)

        XCTAssert(array == [1, 4, 2, 3])
    }

    func testInsert_Successful_AtEnd() {
        var array = [1, 2, 3]

        array.insert(4, safeAt: 3)

        XCTAssert(array == [1, 2, 3, 4])
    }

    func testInsert_Failure() {
        var array = [1, 2, 3]

        array.insert(4, safeAt: 5)

        XCTAssert(array == [1, 2, 3])
    }

    func testGet_Successful() {
        var array = [1, 2, 3]

        let element = array[safe: 1]

        XCTAssert(element == 2)
    }

    func testGet_Failure() {
        var array = [1, 2, 3]

        let element = array[safe: 4]

        XCTAssert(element == nil)
    }

    func testSet_Successful() {
        var array = [1, 2, 3]

        array[safe: 1] = 4

        XCTAssert(array == [1, 4, 3])
    }

    func testSet_Successful_AtEnd() {
        var array = [1, 2, 3]

        array[safe: 3] = 4

        XCTAssert(array == [1, 2, 3, 4])
    }

    func testSet_Failure() {
        var array = [1, 2, 3]

        array[safe: 4] = 4

        XCTAssert(array == [1, 2, 3])
    }
}
Ivan Rep
sumber
3
extension Array {
  subscript (safe index: UInt) -> Element? {
    return Int(index) < count ? self[Int(index)] : nil
  }
}

Menggunakan Above disebutkan ekstensi kembali nil jika indeks kapan saja keluar dari batas.

let fruits = ["apple","banana"]
print("result-\(fruits[safe : 2])")

hasil - nihil

Vinayak Pal
sumber
3

Saya menyadari ini adalah pertanyaan lama. Saya menggunakan Swift5.1 pada titik ini, OP untuk Swift 1 atau 2?

Saya membutuhkan sesuatu seperti ini hari ini, tetapi saya tidak ingin menambahkan ekstensi skala penuh hanya untuk satu tempat dan ingin sesuatu yang lebih fungsional (lebih aman?). Saya juga tidak perlu melindungi terhadap indeks negatif, hanya indeks yang mungkin melewati akhir array:

let fruit = ["Apple", "Banana", "Coconut"]

let a = fruit.dropFirst(2).first // -> "Coconut"
let b = fruit.dropFirst(0).first // -> "Apple"
let c = fruit.dropFirst(10).first // -> nil

Bagi mereka yang berdebat tentang Urutan dengan nol, apa yang Anda lakukan tentang firstdan lastproperti yang mengembalikan nol untuk koleksi kosong?

Saya suka ini karena saya bisa mengambil barang yang ada dan menggunakannya untuk mendapatkan hasil yang saya inginkan. Saya juga tahu bahwa dropFirst (n) bukan salinan seluruh koleksi, hanya sepotong. Dan kemudian perilaku pertama yang sudah ada mengambil alih untuk saya.

Travis Griggs
sumber
1

Saya pikir ini bukan ide yang baik. Tampaknya lebih baik untuk membangun kode padat yang tidak menghasilkan mencoba menerapkan indeks out-of-bounds.

Harap pertimbangkan bahwa kesalahan seperti itu gagal diam-diam (seperti yang disarankan oleh kode Anda di atas) dengan kembali nilcenderung menghasilkan kesalahan yang lebih rumit dan lebih sulit diselesaikan.

Anda bisa melakukan override dengan cara yang sama seperti yang Anda gunakan dan hanya menulis subskrip dengan cara Anda sendiri. Hanya kekurangannya adalah kode yang ada tidak akan kompatibel. Saya pikir untuk menemukan hook untuk mengganti generik x [i] (juga tanpa preprocessor teks seperti pada C) akan sulit.

Yang paling dekat yang bisa saya pikirkan adalah

// compile error:
if theIndex < str.count && let existing = str[theIndex]

EDIT : Ini benar-benar berfungsi. Satu-baris !!

func ifInBounds(array: [AnyObject], idx: Int) -> AnyObject? {
    return idx < array.count ? array[idx] : nil
}

if let x: AnyObject = ifInBounds(swiftarray, 3) {
    println(x)
}
else {
    println("Out of bounds")
}
Mundi
sumber
6
Saya tidak setuju - maksud dari pengikatan opsional adalah hanya berhasil jika kondisi terpenuhi. (Untuk opsional, itu berarti ada nilai.) Menggunakan if letdalam hal ini tidak membuat program lebih kompleks, atau kesalahan lebih sulit dipecahkan. Ini hanya mengembun ifcek batas dua pernyataan tradisional dan pencarian aktual menjadi pernyataan tunggal, kental. Ada kasus-kasus (terutama yang bekerja di UI) di mana indeks normal berada di luar batas, seperti meminta NSTableViewuntuk selectedRowtanpa pilihan.
Craig Otis
3
@Mundi ini tampaknya menjadi komentar, bukan jawaban untuk pertanyaan OP.
jlehr
1
@CraigOtis Tidak yakin saya setuju. Anda dapat menulis cek ini secara ringkas dalam "baris tunggal, pernyataan kental", misalnya menggunakan countElementsatau seperti yang OP lakukan count, hanya saja tidak dengan cara bahasa mendefinisikan penulisan larik subscript.
Mundi
1
@ jlehr Mungkin tidak. Adalah permainan yang adil untuk mempertanyakan niat atau kebijaksanaan dari masalah yang diajukan.
Mundi
2
@Mundi Heh, terutama jika nanti Anda mengeditnya untuk benar-benar menjawab pertanyaan. :-)
jlehr
1

Saya telah mengisi array dengan nils dalam kasus penggunaan saya:

let components = [1, 2]
var nilComponents = components.map { $0 as Int? }
nilComponents += [nil, nil, nil]

switch (nilComponents[0], nilComponents[1], nilComponents[2]) {
case (_, _, .Some(5)):
    // process last component with 5
default:
    break
}

Juga periksa ekstensi subskrip dengan safe:label oleh Erica Sadun / Mike Ash: http://ericasadun.com/2015/06/01/swift-safe-array-indexing-my-favorite-thing-of-the-new-week/

Marián Černý
sumber
0

"Common Rejected Changes" untuk Swift list berisi penyebutan tentang mengubah akses subscript Array untuk mengembalikan opsional daripada mogok:

Lakukan Array<T>pengembalian akses subskrip T?atau T!sebagai ganti T: Perilaku array saat ini disengaja , karena secara akurat mencerminkan fakta bahwa akses array di luar batas adalah kesalahan logika. Mengubah perilaku saat ini akan memperlambat Arrayakses ke tingkat yang tidak dapat diterima. Topik ini telah muncul beberapa kali sebelumnya tetapi sangat tidak mungkin untuk diterima.

https://github.com/apple/swift-evolution/blob/master/commonly_proposed.md#strings-characters-and-collection-types

Jadi akses subskrip dasar tidak akan berubah untuk mengembalikan opsional.

Namun, tim / komunitas Swift tampaknya terbuka untuk menambahkan pola akses opsional-kembali baru ke Array, baik melalui fungsi atau subskrip.

Ini telah diusulkan dan dibahas di forum Swift Evolution di sini:

https://forums.swift.org/t/add-accessor-with-bounds-check-to-array/16871

Khususnya, Chris Lattner memberi ide "+1":

Setuju, ejaan yang paling sering disarankan untuk ini adalah: yourArray[safe: idx] :, yang menurut saya bagus. Saya sangat +1 untuk menambahkan ini.

https://forums.swift.org/t/add-accessor-with-bounds-check-to-array/16871/13

Jadi ini dimungkinkan di luar kotak di beberapa versi Swift yang akan datang. Saya akan mendorong siapa saja yang ingin berkontribusi pada utas Swift Evolution itu.

pkamb
sumber
0

Untuk menyebarkan mengapa operasi gagal, kesalahan lebih baik daripada opsional. Subskrip tidak dapat membuang kesalahan, jadi itu harus menjadi metode.

public extension Collection {
  /// - Returns: same as subscript, if index is in bounds
  /// - Throws: CollectionIndexingError
  func element(at index: Index) throws -> Element {
    guard indices.contains(index)
    else { throw CollectionIndexingError() }

    return self[index]
  }
}

/// Thrown when `element(at:)` is called with an invalid index.
public struct CollectionIndexingError: Error { }
XCTAssertThrowsError( try ["🐾", "🥝"].element(at: 2) )

let optionals = [1, 2, nil]
XCTAssertEqual(try optionals.element(at: 0), 1)

XCTAssertThrowsError( try optionals.element(at: optionals.endIndex) )
{ XCTAssert($0 is CollectionIndexingError) }
Jessy
sumber
0

Tidak yakin mengapa tidak ada seorang pun, telah memasang ekstensi yang juga memiliki setter untuk menumbuhkan array secara otomatis

extension Array where Element: ExpressibleByNilLiteral {
    public subscript(safe index: Int) -> Element? {
        get {
            guard index >= 0, index < endIndex else {
                return nil
            }

            return self[index]
        }

        set(newValue) {
            if index >= endIndex {
                self.append(contentsOf: Array(repeating: nil, count: index - endIndex + 1))
            }

            self[index] = newValue ?? nil
        }
    }
}

Penggunaannya mudah dan bekerja mulai dari Swift 5.1

var arr:[String?] = ["A","B","C"]

print(arr) // Output: [Optional("A"), Optional("B"), Optional("C")]

arr[safe:10] = "Z"

print(arr) // [Optional("A"), Optional("B"), Optional("C"), nil, nil, nil, nil, nil, nil, nil, Optional("Z")]

Catatan: Anda harus memahami biaya kinerja (baik dalam waktu / ruang) ketika menumbuhkan array di swift - tetapi untuk masalah kecil kadang-kadang Anda hanya perlu membuat Swift untuk berhenti Swifting sendiri di kaki

Shaheen Ghiassy
sumber
-1

Saya telah membuat ekstensi sederhana untuk array

extension Array where Iterator.Element : AnyObject {
    func iof (_ i : Int ) -> Iterator.Element? {
        if self.count > i {
            return self[i] as Iterator.Element
        }
        else {
            return nil
        }
    }

}

ini bekerja dengan sempurna seperti yang dirancang

Contoh

   if let firstElemntToLoad = roots.iof(0)?.children?.iof(0)?.cNode, 
Mohamed Deux
sumber
-1

Swift 5 Penggunaan

extension WKNavigationType {
    var name : String {
        get {
            let names = ["linkAct","formSubm","backForw","reload","formRelo"]
            return names.indices.contains(self.rawValue) ? names[self.rawValue] : "other"
        }
    }
}

berakhir dengan tetapi benar - benar ingin melakukan umumnya suka

[<collection>][<index>] ?? <default>

tapi karena koleksinya kontekstual, kurasa itu pantas.

slashlos
sumber
Bagaimana jawaban ini berbeda dari yang diterima? Sedangkan bagi saya, tampilannya persis sama (duplikat).
Legonaftik
-1

Ketika Anda hanya perlu mendapatkan nilai dari array dan Anda tidak keberatan dengan penalti kinerja kecil (yaitu jika koleksi Anda tidak besar), ada alternatif berbasis Kamus yang tidak melibatkan (terlalu umum, untuk rasa) ekstensi koleksi:

// Assuming you have a collection named array:
let safeArray = Dictionary(uniqueKeysWithValues: zip(0..., array))
let value = safeArray[index] ?? defaultValue;
Sorin Dolha
sumber