Ustalanie, czy słownik Swift zawiera klucz i uzyskiwanie którejkolwiek z jego wartości

260

Obecnie używam następujących (niezdarnych) fragmentów kodu do ustalenia, czy (niepusty) słownik Swift zawiera dany klucz i do uzyskania jednej (dowolnej) wartości z tego samego słownika.

Jak bardziej elegancko umieścić to w Swift?

// excerpt from method that determines if dict contains key
if let _ = dict[key] {
    return true
}
else {
    return false
}

// excerpt from method that obtains first value from dict
for (_, value) in dict {
    return value
}
Drux
źródło
Możesz użyć, indexForKeyjeśli uważasz, że jest to bardziej jasne i wyraźne; stackoverflow.com/a/29299943/294884
Fattie,
Bardzo często to, co chcesz, jest w zasadzie: tutaj oznacza w zasadzie „jeśli nie ma takiego klawisza, zwraca pustą”. cityName:String = dict["city"] ?? ""?? ""
Fattie

Odpowiedzi:

481

Nie potrzebujesz do tego żadnego specjalnego kodu, ponieważ to właśnie robi już słownik. Gdy pobierasz dict[key], wiesz, czy słownik zawiera klucz, ponieważ opcjonalny, który otrzymasz, nie jest nil(i zawiera wartość).

Jeśli więc chcesz tylko odpowiedzieć na pytanie, czy słownik zawiera klucz, zapytaj:

let keyExists = dict[key] != nil

Jeśli chcesz tę wartość i wiesz, że słownik zawiera klucz, powiedz:

let val = dict[key]!

Ale jeśli, jak to zwykle bywa, nie wiesz, że zawiera on klucz - chcesz go pobrać i użyć, ale tylko jeśli istnieje - użyj czegoś takiego if let:

if let val = dict[key] {
    // now val is not nil and the Optional has been unwrapped, so use it
}
matowy
źródło
6
Nie podążam. Właśnie otrzymałem wartość (dwa razy). Czego jeszcze chcesz?
mat
Nie ma czegoś takiego jak „pierwsza wartość”; słownik jest nieuporządkowany. Jeśli chcesz tylko wartości, bez kluczy, powiedz dict.values.
mat
1
Bądź ostrożny, ponieważ dict.valuesjest nieprzejrzysty. Możesz jeździć na rowerze, ale to wszystko. (Okej, to nie wszystko, ale żartuj.) Jeśli chcesz, aby zmieniło się w tablicę, weź dict.values.array.
mat
1
@bakakazouba Wypróbuj: let d = ["hey":"ho"]; let s = d["hey"]; print(s)Jest to opcja, tak jak zawsze była.
mat.
1
@Kross To bardzo głębokie pytanie, dlatego zadaj je osobno.
mat
68

Dlaczego nie po prostu sprawdzić dict.keys.contains(key)? Sprawdzanie dict[key] != nilnie zadziała w przypadkach, gdy wartość wynosi zero. Jak [String: String?]na przykład w słowniku .

Hans Terje Bakke
źródło
2
Lub zamiast pisać if let val = dict[key], możesz użyć if let val = dict[key] as? Stringw tym przypadku.
Okhan Okbay
.keys.contains nie pomaga odróżnić, czy klucz istnieje, czy wartość jest równa zero. Może we wcześniejszych szybkich wersjach? Nie działa dla mnie w szybkim 4.
Rowan Gontier
8
Jest to potencjalnie bardzo marnotrawstwo, ponieważ (1) dict.keyswywołujesz tablicę ze wszystkimi kluczami, a następnie (2) sprawdza kolejno każdy klucz, aż znajdziesz jeden (lub żaden!). Zdaję sobie sprawę, że próbujesz zająć się przypadkiem [String: String?], ale bardzo ostrożnie podchodziłbyś do tego rozwiązania ...
Charles
3
Jak wspomniano w @charles, wyeliminowałoby to korzyści szybkiego wyszukiwania, jakie zapewnia korzystanie ze słownika, więc odradzam tę, ale zdecydowanie jest to ważna opcja.
businesscasual
4
Nigdy nie należy używać tej metody, ponieważ ma złożoność O (n) zamiast teoretycznej (zależy od implementacji funkcji skrótu) O (1) bezpośredniego dostępu do słownika.
Krypt
45

Akceptowana odpowiedź let keyExists = dict[key] != nilnie zadziała, jeśli Słownik zawiera klucz, ale ma wartość zero.

Jeśli chcesz mieć pewność, że słownik nie zawiera klucza, skorzystaj z niego (testowane w Swift 4).

if dict.keys.contains(key) {
  // contains key
} else { 
  // does not contain key
}
bergy
źródło
3
To jest to samo, co odpowiedź Hana.
Declan McKenna
1
Sprawdzanie == nildziała, nawet jeśli słownik ma opcjonalne wartości. Wynika to z tego, że wynik wyszukiwania jest zawijany jako Optional. Z drugiej strony, aby sprawdzić, czy szukana wartość jest rzeczywiście nilraczej nieobecna, możesz użyć == .some(nil).
jedwidz
1
Dodałbym efektywność korzystania z tej metody w porównaniu do wyszukiwania O (1) innych metod. Myślę, że to wyszukiwanie O (n)
Alberto Vega
1
@ AlbertoVega-MSFT It's O(1). Zobacz także: github.com/apple/swift-evolution/blob/master/propozycje/… Co sprawiło, że tak myślisz O(n)?
Alexander - Przywróć Monikę
1
Dokumentacja tutajDictionary.Keys.contains(...) funkcji mówi, że ma złożoność, gdzie jest długość sekwencji. O(n)n
Adil Hussain
5

Wygląda na to, że masz to, czego potrzebujesz od @matt, ale jeśli chcesz szybko uzyskać wartość klucza lub po prostu pierwszą wartość, jeśli ten klucz nie istnieje:

extension Dictionary {
    func keyedOrFirstValue(key: Key) -> Value? {
        // if key not found, replace the nil with 
        // the first element of the values collection
        return self[key] ?? first(self.values)
        // note, this is still an optional (because the
        // dictionary could be empty)
    }
}

let d = ["one":"red", "two":"blue"]

d.keyedOrFirstValue("one")  // {Some "red"}
d.keyedOrFirstValue("two")  // {Some "blue"}
d.keyedOrFirstValue("three")  // {Some "red”}

Uwaga: nie ma gwarancji, co faktycznie otrzymasz jako pierwszą wartość, po prostu w tym przypadku zwraca się „czerwony”.

Prędkość lotu
źródło
1
dobre wykorzystanie ??operatora odebrało mi moje wygodne rozwiązanie, ale jako rozszerzenie tej wartości domyślnej może pojawić się niepewność danych lub nieoczekiwane zachowania. To brzmi jak coś, co Dictionarypowinna zastosować konkretna podklasa . Jak często potrzebujesz pierwszej wartości w przypadku nilzwrotu z wyłączeniem funkcji specyficznych dla sytuacji?
NoodleOfDeath
1
if dictionayTemp["quantity"] != nil
    {

  //write your code
    }
Paresh Hirpara
źródło
0

Moje rozwiązanie dla implementacji pamięci podręcznej przechowującej opcjonalne NSAttributString:

public static var attributedMessageTextCache    = [String: NSAttributedString?]()

    if attributedMessageTextCache.index(forKey: "key") != nil
    {
        if let attributedMessageText = TextChatCache.attributedMessageTextCache["key"]
        {
            return attributedMessageText
        }
        return nil
    }

    TextChatCache.attributedMessageTextCache["key"] = .some(.none)
    return nil
Rishi
źródło
0

Oto, co działa dla mnie w Swift 3

let _ = (dict[key].map { $0 as? String } ?? "")
ΩlostA
źródło