Swift Dizisi - Bir dizinin mevcut olup olmadığını kontrol edin


140

Swift'te, ölümcül hata oluşmadan dizide bir dizinin var olup olmadığını kontrol etmenin herhangi bir yolu var mı?

Böyle bir şey yapabileceğimi umuyordum:

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
}

Ama anladım

ölümcül hata: Dizi dizini aralık dışında

Yanıtlar:


442

Swift'te zarif bir yol:

let isIndexValid = array.indices.contains(index)

10
Gerçek hayatta önemli değil ama zaman karmaşıklığı açısından akıllıca kullanmak daha iyi olmaz mıydı index < array.count?
funct7

2
Dürüst olmak gerekirse, negatif bir indeks değeri yaşamadığım için verdiğiniz örnek biraz daha tutarlı olduğunu düşünüyorum, ancak bu gerçekten durum böyle olsaydı, iki doğru / yanlış kontrol hala daha iyi olurdu: yani index >= 0 && index < array.counten kötü durum n yerine karşılaştırmaları.
funct7

13
Hız farkının ne olduğunu merak ediyorsanız, Xcode'un araçlarıyla ölçtüm ve ihmal edilebilir. gist.github.com/masonmark/a79bfa1204c957043687f4bdaef0c2ad
Mason

7
Sadece bunun neden doğru olduğuna başka bir nokta daha eklemek için: bir üzerinde çalışırken aynı mantığı uygularsanız ArraySlice, ilk dizin 0 olmaz, bu yüzden yapmak index >= 0yeterli bir kontrol olmaz. .indicesbunun yerine her durumda çalışır.
DeFrenZ

2
Bunun için Swift'i seviyorum. İşte benim kullanım durum: hizmet için boktan JSON yapısı bina bunu yapmak zorunda kaldı: "attendee3": names.indices.contains (2)? isimler [2]: ""
Lee Probert

64

Tip uzantısı:

extension Collection {

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

}

Bunu kullanarak, dizininize isteğe bağlı anahtar kelimeyi eklerken isteğe bağlı bir değer elde edersiniz. Bu, dizin aralık dışında olsa bile programınızın çökmediği anlamına gelir. Örneğinizde:

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
Mükemmel cevap parameter En önemlisi optionalparametrede kullanılırken okunabilir . Teşekkürler!
Jakub

2
Awesomeness: D Günümü kurtardım.
Codetard

2
Bu güzel
JoeGalind

32

Dizinin dizi boyutundan küçük olup olmadığını kontrol etmeniz yeterlidir:

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

3
Dizi boyutu bilinmiyorsa ne olur?
Nathan McKaskle

8
@NathanMcKaskle Dizi her zaman kaç öğe içerdiğini bilir, bu yüzden boyut bilinemez
Antonio

16
Üzgünüm orada düşünmüyordum. Sabah kahvesi hala kan dolaşımına giriyordu.
Nathan McKaskle

4
@NathanMcKaskle endişelenme ... bazen birkaç kahveden sonra bile başıma geliyor ;-)
Antonio

Bazı açılardan, bu en iyi cevaptır, çünkü O (n) yerine O (1) 'de çalışır.
ScottyBlades

12

Biraz uzatma şekeri ekleyin:

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 letDizilerle birlikte stil kodlaması yaparken okunabilirliği artırır


2
dürüst olmak gerekirse, bu bunu maksimum okunabilirlik ve netlik ile yapmanın en hızlı yoludur
barndog

1
@barndog Çok seviyorum. Bunu genellikle Başlattığım herhangi bir projede Şeker olarak eklerim. Muhafız örneği de eklendi. Bu işe geldiği için hızlı gevşek topluluğa teşekkür ederiz.
eonist

iyi bir çözüm ... ama çıktı verdiğiniz "d" yazdıracak ...
jayant rawat

Bu Swift dilinin bir parçası olmalıdır. Endeksin aralık dışında olması sorunu sıfır değerlerden daha az sorunlu değildir. Hatta benzer sözdizimi kullanmanızı öneririz: belki gibi bir şey: myArray [? 3]. MyArray isteğe bağlıysa, myArray? [? 3]
Andy Weinstein

@Andy Weinstein Bunu elma önermelisin 💪
eonist

7

Dizinin boyutunu kontrol etmek için bunu daha güvenli bir şekilde yeniden yazabilir ve üçlü koşullu kullanabilirsiniz:

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

1
Antonio'nun önerdiği şey çok daha saydam (ve verimli) Üçlemenin uygun olacağı yerde, eğer if'i birlikte kullanmak ve ortadan kaldırmak için hazır bir değeriniz varsa, bir let ifadesi bırakarak.
David Berry

1
@David Antonio'nun önerdiği şey , orijinal koddaki ifbir ififade yerine iki ifade gerektirir . Kodum ikinciyi ifkoşullu bir işleçle değiştirerek, elseiki ayrı elsebloğu zorlamak yerine tekli kalmanızı sağlar .
dasblinkenlight

1
Onun kodunda iki if ifadeleri görmüyorum, onun elips için let str2 = arr [1] koymak ve bitti. OP if let deyimini antonio's if deyimiyle değiştirirseniz ve ödevi içeri taşırsanız (ya da değiştirmezseniz, str2'nin tek kullanımı bunu yazdırmak olduğundan, gerek yoktur, yalnızca yazdırma işlemini satır satırına yerleştirin. üçlü operatörün if / else için sadece (bazılarına :)) belirsiz olduğunu unutmayın. Varsa. count> 2, o zaman arr [2] asla nil olamaz, neden String ile eşlesin? sadece başka bir if deyimi uygulayabilirsiniz.
David Berry

@David ifOP'nin tüm sorusu, Antonio'nun cevabının "o zaman" dalının içinde sona erecek, böylece iki iç içe geçmiş olacaktı if. Ben OPs kodunu küçük bir örnek olarak görüyorum, bu yüzden hala bir isteyeceğini varsayıyorum if. Onun örneğinde ifgerekli olmadığını kabul ediyorum . Ama sonra tekrar, tüm ifade anlamsızdır, çünkü OP dizinin yeterli uzunluğa sahip olmadığını ve öğelerinin hiçbirinin olmadığını bilir nil, böylece ifsadece elsebloğunu kaldırabilir ve tutabilir .
dasblinkenlight

Hayır olmazdı. Antonio's if OP'nin if ifadesinin yerini alıyor. Dizi türü [String] olduğundan, hiçbir zaman bir nil içeremeyeceğini bilirsiniz, bu nedenle uzunluğun ötesinde bir şey kontrol etmeniz gerekmez.
David Berry

7

Swift 4 uzantısı:

Benim için böyle bir yöntemi tercih ederim.

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


1

Bir dizi dizininin var olduğunu iddia etmek:

Uzatma şekeri eklemek istemiyorsanız bu yöntem harika:

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
    }

}

İndexOutOfBounds'u işlemek benim için iş .


Endeks negatifse ne olur? Muhtemelen bunu da kontrol etmelisiniz.
Frankie Simon
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.