Déterminer si le dictionnaire Swift contient une clé et obtenir l'une de ses valeurs

260

J'utilise actuellement les morceaux de code (maladroits) suivants pour déterminer si un dictionnaire Swift (non vide) contient une clé donnée et pour obtenir une (toute) valeur à partir du même dictionnaire.

Comment peut-on mettre cela plus élégamment dans 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
la source
Vous pouvez l'utiliser indexForKeysi vous pensez que c'est plus clair et plus explicite; stackoverflow.com/a/29299943/294884
Fattie
Très souvent, ce que vous voulez est fondamentalement: cityName:String = dict["city"] ?? "" le ?? ""ici signifie essentiellement "s'il n'y a pas une telle clé, retournez un blanc".
Fattie

Réponses:

481

Vous n'avez pas besoin de code spécial pour cela, car c'est déjà ce que fait un dictionnaire. Lorsque vous récupérez, dict[key]vous savez si le dictionnaire contient la clé, car l'option facultative que vous récupérez n'est pasnil (et il contient la valeur).

Donc, si vous voulez simplement répondre à la question de savoir si le dictionnaire contient la clé, demandez:

let keyExists = dict[key] != nil

Si vous voulez la valeur et que vous savez que le dictionnaire contient la clé, dites:

let val = dict[key]!

Mais si, comme cela arrive habituellement, vous ne savez pas qu'elle contient la clé - vous voulez la récupérer et l'utiliser, mais seulement si elle existe - alors utilisez quelque chose comme if let:

if let val = dict[key] {
    // now val is not nil and the Optional has been unwrapped, so use it
}
mat
la source
6
Je ne suis pas. Je viens d'obtenir une valeur (deux fois). Que veux-tu de plus?
mat
Il n'y a pas de «première valeur»; un dictionnaire n'est pas ordonné. Si vous voulez juste les valeurs, sans les clés, dites dict.values.
mat
1
Soyez prudent car il dict.valuesest opaque. Vous pouvez le parcourir, mais c'est tout. (D'accord, ce n'est pas tout, mais faites-moi plaisir.) Si vous voulez qu'il soit réifié en tableau, prenez dict.values.array.
mat
1
@bubakazouba Essayez ceci: let d = ["hey":"ho"]; let s = d["hey"]; print(s)c'est une option, comme cela a toujours été le cas.
mat
1
@Kross C'est une question très profonde mais veuillez donc la poser séparément.
mat
68

Pourquoi ne pas simplement vérifier dict.keys.contains(key)? La vérification de dict[key] != nilne fonctionnera pas dans les cas où la valeur est nulle. Comme avec un dictionnaire [String: String?]par exemple.

Hans Terje Bakke
la source
2
Ou au lieu de simplement écrire, if let val = dict[key]vous pouvez utiliser if let val = dict[key] as? Stringdans ce cas.
Okhan Okbay
.keys.contains n'aide pas à distinguer si la clé existe ou si la valeur est nulle. Peut-être dans les versions plus rapides de Swift? Ne fonctionne pas pour moi dans swift 4.
Rowan Gontier
8
C'est potentiellement très inutile, car (1) vous appelez dict.keysqui crée un tableau avec toutes les clés, puis (2) vérifie chaque clé dans l'ordre jusqu'à ce que vous en trouviez une (ou aucune!). Je me rends compte que vous essayez d'aborder le cas d'un [String: String?], mais seriez très prudent avec cette solution ...
Charles
3
Comme @charles l'a mentionné, cela éliminerait l'avantage de la recherche rapide qu'offre l'utilisation d'un dictionnaire, donc je déconseille celui-ci, mais c'est certainement une option valide.
businesscasual
4
Vous ne devriez jamais utiliser cette méthode, a une complexité O (n) au lieu de théorique (dépend de l'implémentation de la fonction de hachage) O (1) d'accès direct au dictionnaire.
Krypt
45

La réponse acceptée let keyExists = dict[key] != nilne fonctionnera pas si le dictionnaire contient la clé mais a une valeur nulle.

Si vous voulez être sûr que le dictionnaire ne contient pas du tout la clé, utilisez ceci (testé dans Swift 4).

if dict.keys.contains(key) {
  // contains key
} else { 
  // does not contain key
}
bergé
la source
3
C'est la même chose que la réponse de Han.
Declan McKenna
1
La vérification == nilfonctionne, même si le dictionnaire a des valeurs facultatives. Cela est dû au fait que le résultat de la recherche est encapsulé en tant que Optional. D'autre part, pour vérifier si la valeur recherchée est vraiment nilplutôt qu'absente, vous pouvez utiliser == .some(nil).
jedwidz
1
J'ajouterais l'efficacité de l'utilisation de cette méthode par rapport à la recherche O (1) des autres méthodes. Je crois que c'est la recherche O (n)
Alberto Vega
1
@ AlbertoVega-MSFT C'est O(1). Voir aussi: github.com/apple/swift-evolution/blob/master/proposals/… Qu'est-ce qui vous a fait croire que c'était O(n)?
Alexander - Reinstate Monica
1
La documentation de la Dictionary.Keys.contains(...)fonction indique ici qu'elle est O(n)complexe, où nest la longueur de la séquence.
Adil Hussain
5

On dirait que vous avez obtenu ce dont vous avez besoin de @matt, mais si vous voulez un moyen rapide d'obtenir une valeur pour une clé, ou tout simplement la première valeur si cette clé n'existe pas:

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

Remarque, aucune garantie de ce que vous obtiendrez réellement comme première valeur, il se produit juste dans ce cas de retourner "rouge".

Vitesse de vitesse
la source
1
une bonne utilisation de l' ??opérateur m'a pris ma solution de commodité, mais en tant qu'extension, cette valeur par défaut pourrait présenter des insécurités de données ou des comportements inattendus. Cela ressemble à quelque chose qu'une sous-classe concrète Dictionarydevrait utiliser. À quelle fréquence avez-vous besoin de la première valeur en cas de nilretour hors fonctionnalité spécifique à la situation?
NoodleOfDeath
1
if dictionayTemp["quantity"] != nil
    {

  //write your code
    }
Paresh Hirpara
la source
0

Ma solution pour une implémentation de cache qui stocke NSAttributedString en option:

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
la source
0

Voici ce qui fonctionne pour moi sur Swift 3

let _ = (dict[key].map { $0 as? String } ?? "")
ΩlostA
la source