Bestimmen, ob das Swift-Wörterbuch einen Schlüssel enthält, und Abrufen eines seiner Werte


260

Ich verwende derzeit die folgenden (ungeschickten) Codeteile, um festzustellen, ob ein (nicht leeres) Swift-Wörterbuch einen bestimmten Schlüssel enthält, und um einen (beliebigen) Wert aus demselben Wörterbuch zu erhalten.

Wie kann man das eleganter in Swift setzen?

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

Sie können verwenden, indexForKeywenn Sie der Meinung sind, dass es klarer und expliziter ist. stackoverflow.com/a/29299943/294884
Fattie

Sehr oft ist das, was Sie wollen, im Grunde: cityName:String = dict["city"] ?? "" Das ?? ""hier bedeutet im Grunde "wenn es keinen solchen Schlüssel gibt, geben Sie ein Leerzeichen zurück".
Fattie

Antworten:


481

Dazu benötigen Sie keinen speziellen Code, da dies bereits in einem Wörterbuch der Fall ist. Wenn Sie abrufen dict[key], wissen Sie , ob das Wörterbuch den Schlüssel enthält, da die Option, die Sie zurückerhalten, nicht vorhanden ist nil(und den Wert enthält).

Wenn Sie also nur die Frage beantworten möchten, ob das Wörterbuch den Schlüssel enthält, fragen Sie:

let keyExists = dict[key] != nil

Wenn Sie den Wert möchten und wissen, dass das Wörterbuch den Schlüssel enthält, sagen Sie:

let val = dict[key]!

Wenn Sie jedoch wie gewöhnlich nicht wissen, dass der Schlüssel enthalten ist - Sie möchten ihn abrufen und verwenden, aber nur, wenn er vorhanden ist -, verwenden Sie Folgendes if let:

if let val = dict[key] {
    // now val is not nil and the Optional has been unwrapped, so use it
}

6
Ich folge nicht. Ich habe gerade einen Wert erhalten (zweimal). Was willst du noch?
Matt

Es gibt keinen "ersten Wert"; Ein Wörterbuch ist ungeordnet. Wenn Sie nur die Werte ohne Schlüssel möchten, sagen wir dict.values.
Matt

1
Seien Sie vorsichtig, weil dict.valueses undurchsichtig ist. Sie können es durchfahren, aber das ist alles. (Okay, es ist nicht alles, aber humor mich.) Wenn Sie möchten, dass es zu einem Array geändert wird, nehmen Sie dict.values.array.
Matt

1
@bubakazouba Versuchen let d = ["hey":"ho"]; let s = d["hey"]; print(s)Sie Folgendes: Es ist optional, so wie es immer war.
Matt

1
@Kross Das ist eine sehr tiefe Frage, aber bitte stellen Sie sie separat.
Matt

68

Warum nicht einfach nachsehen dict.keys.contains(key)? Das Überprüfen auf dict[key] != nilfunktioniert nicht in Fällen, in denen der Wert Null ist. Wie [String: String?]zum Beispiel bei einem Wörterbuch .


2
Oder anstatt nur zu schreiben if let val = dict[key], können Sie if let val = dict[key] as? Stringin diesem Fall verwenden.
Okhan Okbay

.keys.contains hilft nicht zu unterscheiden, ob ein Schlüssel vorhanden ist oder ob der Wert Null ist. Vielleicht in früheren schnellen Versionen? Funktioniert nicht für mich in schnell 4.
Rowan Gontier

8
Dies ist möglicherweise sehr verschwenderisch, da (1) Sie dict.keysein Array mit allen Schlüsseln aufrufen und dann (2) jeden Schlüssel der Reihe nach überprüfen, bis Sie einen (oder keinen!) Finden. Mir ist klar, dass Sie versuchen, den Fall von a anzusprechen [String: String?], aber mit dieser Lösung sehr vorsichtig sein würden ...
Charles

3
Wie bereits erwähnt, würde dies den Vorteil der schnellen Suche beseitigen, den die Verwendung eines Wörterbuchs bietet. Daher würde ich davon abraten, aber definitiv eine gültige Option.
businesscasual

4
Sie sollten diese Methode niemals verwenden, da die Komplexität O (n) anstelle der theoretischen (abhängig von der Implementierung der Hash-Funktion) O (1) des direkten Wörterbuchzugriffs liegt.
Krypt

45

Die akzeptierte Antwort let keyExists = dict[key] != nilfunktioniert nicht, wenn das Wörterbuch den Schlüssel enthält, aber den Wert Null hat.

Wenn Sie sicher sein möchten, dass das Wörterbuch den Schlüssel überhaupt nicht enthält, verwenden Sie diesen (in Swift 4 getestet).

if dict.keys.contains(key) {
  // contains key
} else { 
  // does not contain key
}

3
Dies ist das gleiche wie Hans Antwort.
Declan McKenna

1
Die Überprüfung == nilfunktioniert auch dann, wenn das Wörterbuch optionale Werte enthält. Dies liegt daran, dass das Suchergebnis als Optional. Auf der anderen Seite können Sie verwenden, um zu überprüfen, ob der nachgeschlagene Wert wirklich nilnicht vorhanden ist == .some(nil).
jedwidz

1
Ich würde die Effizienz der Verwendung dieser Methode im Vergleich zur O (1) -Suche der anderen Methoden hinzufügen. Ich glaube, es ist O (n) Lookup
Alberto Vega

1
@ AlbertoVega-MSFT Es ist O(1). Siehe auch: github.com/apple/swift-evolution/blob/master/proposals/… Warum haben Sie das gedacht O(n)?
Alexander - Reinstate Monica

1
Die Dokumentation für die Dictionary.Keys.contains(...)Funktion hier besagt, dass sie O(n)komplex ist, wobei ndie Länge der Sequenz ist.
Adil Hussain

5

Sieht so aus, als hätten Sie von @matt das bekommen, was Sie brauchen, aber wenn Sie schnell einen Wert für einen Schlüssel erhalten möchten, oder nur den ersten Wert, wenn dieser Schlüssel nicht vorhanden ist:

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

Beachten Sie, dass keine Garantie dafür besteht, was Sie tatsächlich als ersten Wert erhalten. In diesem Fall wird lediglich "rot" zurückgegeben.


1
Eine gute Verwendung des ??Operators hat mir meine bequeme Lösung genommen, aber als Erweiterung könnte dieser Standardwert Datenunsicherheiten oder unerwartetes Verhalten darstellen. Das klingt nach etwas, das eine konkrete Unterklasse verwenden Dictionarysollte. Wie oft benötigen Sie den ersten Wert bei einer nilRückgabe ohne situationsspezifische Funktionalität?
NoodleOfDeath


0

Meine Lösung für eine Cache-Implementierung, in der optional NSAttributedString gespeichert ist:

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

0

Folgendes funktioniert bei Swift 3 für mich

let _ = (dict[key].map { $0 as? String } ?? "")
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.