Determinare se il dizionario Swift contiene chiave e ottenere uno dei suoi valori


260

Attualmente sto usando i seguenti (goffi) pezzi di codice per determinare se un dizionario Swift (non vuoto) contiene una determinata chiave e per ottenere un (qualsiasi) valore dallo stesso dizionario.

Come si può mettere questo in modo più elegante in 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
}

Puoi usarlo indexForKeyse ritieni che sia più chiaro ed esplicito; stackoverflow.com/a/29299943/294884
Fattie

Molto spesso ciò che si vuole è fondamentalmente: cityName:String = dict["city"] ?? "" Il ?? ""qui significa fondamentalmente "se non esiste una chiave, restituisce un vuoto".
Fattie,

Risposte:


481

Non hai bisogno di alcun codice speciale per farlo, perché è quello che fa già un dizionario. Quando si recupera dict[key]si sa se il dizionario contiene la chiave, perché l'opzionale che si ottiene indietro non è nil(e contiene il valore).

Quindi, se vuoi solo rispondere alla domanda se il dizionario contiene la chiave, chiedi:

let keyExists = dict[key] != nil

Se vuoi il valore e sai che il dizionario contiene la chiave, dì:

let val = dict[key]!

Ma se, come al solito, non sai che contiene la chiave - vuoi prenderla e usarla, ma solo se esiste - usa qualcosa come if let:

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

6
Non seguo Ho appena ottenuto un valore (due volte). Che cosa vuoi di più?
matt

Non esiste "il primo valore"; un dizionario non è ordinato. Se vuoi solo i valori, senza le chiavi, dì dict.values.
matt

1
Fai attenzione perché dict.valuesè opaco. Puoi passarci attraverso ma è tutto. (Va bene, non è tutto, ma umorizzami.) Se vuoi che sia reificato su un array, prendi dict.values.array.
matt

1
@bubakazouba Prova questo: let d = ["hey":"ho"]; let s = d["hey"]; print(s)è un optional, proprio come è sempre stato.
matt

1
@Kross Questa è una domanda molto profonda ma, per favore, chiedila separatamente.
matt

68

Perché non controllare semplicemente dict.keys.contains(key)? Il controllo per dict[key] != nilnon funzionerà nei casi in cui il valore è zero. Come [String: String?]ad esempio un dizionario .


2
O invece di scrivere semplicemente if let val = dict[key]puoi usare if let val = dict[key] as? Stringin questo caso.
Okhan Okbay,

.keys.contains non aiuta a distinguere se la chiave esiste o se il valore è zero. Forse nelle precedenti versioni veloci? Non funziona per me in rapido 4.
Rowan Gontier

8
Questo è potenzialmente molto dispendioso, perché (1) si chiama dict.keysche crea un array con tutte le chiavi, quindi (2) controlla ogni chiave in ordine fino a quando non ne trovi una (o nessuna!). Mi rendo conto che stai cercando di affrontare il caso di a [String: String?], ma starei molto attento a quella soluzione ...
charles,

3
Come accennato da @charles, questo eliminerebbe il vantaggio della ricerca veloce che offre un dizionario, quindi sconsiglio questo, ma sicuramente è un'opzione valida.
businesscasual

4
Non si dovrebbe mai usare questo metodo, in quanto presenta complessità O (n) anziché teorica (dipende dall'implementazione della funzione hash) O (1) dell'accesso diretto al dizionario.
Krypt,

45

La risposta accettata let keyExists = dict[key] != nilnon funzionerà se il dizionario contiene la chiave ma ha un valore pari a zero.

Se vuoi essere sicuro che il Dizionario non contenga affatto la chiave, utilizzalo (testato in Swift 4).

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

3
Questa è la stessa della risposta di Han.
Declan McKenna,

1
Il controllo == nilfunziona, anche se il dizionario ha valori opzionali. Questo perché il risultato della ricerca è racchiuso in un Optional. D'altra parte, per verificare se il valore cercato è realmente nilpiuttosto che assente, è possibile utilizzare == .some(nil).
jedwidz,

1
Aggiungerei l'efficienza dell'uso di questo metodo rispetto alla ricerca O (1) degli altri metodi. Credo che sia O (n) lookup
Alberto Vega,

1
@ AlbertoVega-MSFT È O(1). Vedi anche: github.com/apple/swift-evolution/blob/master/proposals/… Cosa ti ha fatto pensare che fosse O(n)?
Alexander - Ripristina Monica il

1
La documentazione per la Dictionary.Keys.contains(...)funzione qui dice che ha O(n)complessità, dov'è nla lunghezza della sequenza.
Adil Hussain,

5

Sembra che tu abbia ottenuto quello che ti serve da @matt, ma se vuoi un modo rapido per ottenere un valore per una chiave, o solo il primo valore se quella chiave non esiste:

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

Nota, nessuna garanzia di ciò che effettivamente otterrai come primo valore, in questo caso capita solo di restituire "rosso".


1
il buon uso ??dell'operatore mi ha tolto la mia soluzione di convenienza, ma come estensione quel valore predefinito potrebbe presentare insicurezze dei dati o comportamenti imprevisti. Sembra qualcosa che Dictionarydovrebbe essere impiegato da una sottoclasse concreta . Con quale frequenza è necessario il primo valore in caso di nilrestituzione escludendo la funzionalità specifica della situazione?
NoodleOfDeath,


0

La mia soluzione per un'implementazione della cache che memorizza NSAttributedString opzionale:

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

Ecco cosa funziona per me su Swift 3

let _ = (dict[key].map { $0 as? String } ?? "")
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.