Comment vérifier si un NSDictionary ou NSMutableDictionary contient une clé?


456

Je dois vérifier si un dict a une clé ou non. Comment?


4
(Juste pour quiconque google ici. Notez que c'est une très vieille question . La réponse de SaintMacintosh ci-dessous est l'état de l'art, elle a cinq ans d'avance sur ce contrôle qualité. J'espère que cela aide.)
Fattie

1
En fait, la réponse d'Andy Dent donne également l'état de l'art et plus de contexte. Et il l'a fait plus tôt que SaintMacintosh. Faites-vous plaisir et faites défiler un peu plus.
Peter Kämpf

1
Il utilise: keysByName [test]! = Nil la vérification! = Nil est redondante et à mon humble avis moins lisible. Je voulais juste partager la version TL; DR pour les personnes qui recherchent la syntaxe.
Andrew Hoos

Je suis d'accord avec @SaintMacintosh. sa réponse est beaucoup plus succincte.
Steve Schwarcz

Si vous souhaitez vérifier si le NSDictionarycontient une clé (non spécifique), vous devez utiliser [dictionary allKeys].count == 0Si le countest 0il n'y a pas de clés dans le NSDictionary.
Aleksander Azizi

Réponses:


768

objectForKey retournera nil si une clé n'existe pas.


29
Et si la clé existe, mais sa valeur correspondante est nulle?
Fyodor Soikin

232
Ce n'est pas possible. Vous ne pouvez pas ajouter zéro à un NSDictionary. Vous devrez utiliser à la [NSNull null]place.
Ole Begemann

10
@fyodor an nsdictionay lèvera une NSInvalidArgumentException si vous essayez d'insérer une valeur nulle, donc il ne devrait jamais y avoir de cas où une clé existe, mais la valeur correspondante est nulle.
Brad The App Guy

3
Ne voulez-vous pas utiliser valueForKey, car cela appellerait objectForKey si nécessaire?
Raffi Khatchadourian

6
Vous ne voulez JAMAIS JAMAIS utiliser valueForKey pour un dictionnaire JSON. C'est parce que JSON peut contenir n'importe quelle chaîne comme clé. Par exemple, s'il contient "@count" comme clé, alors objectForKey: @ "@ count" donnera la valeur correcte, mais valueForKey: @ "@ count" donnera le nombre de paires clé / valeur.
gnasher729

162
if ([[dictionary allKeys] containsObject:key]) {
    // contains key
}

ou

if ([dictionary objectForKey:key]) {
    // contains object
}

100
L'exemple un dans cette réponse est lent.
James Van Boxtel

5
lire: l'exemple un dans cette réponse devrait être considéré comme illégal (O (n) au lieu de O (1))
Hertzel Guinness

5
la performance est très pertinente. Il peut être très vrai que cette ligne exacte n'est pas pertinente, mais si elle doit être répétée n fois, tout d'un coup au lieu de prendre n fois sa prise n * m et vous ne pouvez pas comprendre pourquoi votre programme est lent. Presque tout est rapide une fois ou dans de petits cas. Mais à mesure que les programmes grandissent en utilisant la mauvaise structure de données (comme un tableau au lieu de dict dans le premier exemple), cela vous coûtera cher.
Andrew Hoos

2
La vérification d'un dictionnaire (ou d'un ensemble) pour l'existence d'une clé est attendue O (1) avec le pire cas de O (n). Pas O (log (n)). La documentation d'Apple l'explique clairement.
Andrew Hoos

@JoeBlow «animez les points 3D Mecanim» Il semble que vous confondez Cocoa Touch / Objective-C avec Unity Engine / C #. Il est vrai que le moteur Unity est terriblement inefficace sur les plates-formes sur lesquelles il fonctionne. Cependant, il n'est pas du tout vrai que les applications Objective-C / Swift soient intrinsèquement inefficaces, ni que la plupart des développeurs iOS chevronnés ne soient pas activement conscients de l'efficacité de leur code. Quant à «ne concerne que les (4 vivants) programmeurs de performances» - vous n'êtes clairement pas un développeur de jeux. De superbes graphismes et un gameplay à 60 images / s sans tuer la batterie est un défi permanent.
Slipp D. Thompson

98

Les versions plus récentes d'Objective-C et de Clang ont une syntaxe moderne pour cela:

if (myDictionary[myKey]) {

}

Il n'est pas nécessaire de vérifier l'égalité avec nil, car seuls les objets Objective-C non nuls peuvent être stockés dans des dictionnaires (ou des tableaux). Et tous les objets Objective-C sont des valeurs véridiques. Même @NO, @0et [NSNull null]évaluez comme vrai.

Edit: Swift est maintenant une chose.

Pour Swift, vous essayez quelque chose comme le suivant

if let value = myDictionary[myKey] {

}

Cette syntaxe n'exécutera le bloc if que si myKey est dans le dict et si c'est le cas, la valeur est stockée dans la variable value. Notez que cela fonctionne même pour les valeurs de falsey comme 0.


Merci pour cela! Je suis juste curieux, mais je ne trouve pas ce comportement documenté dans la référence de classe objective-c developer.apple.com/library/mac/documentation/Cocoa/Reference/… AM Je suis juste aveugle?
irh

2
Non tu n'es pas aveugle. Ce n'est pas souligné. Mais visible ici (clang) et ici (objc moderne: tout en bas) et ici (guide des collections: dictionnaire de recherche)
Andrew Hoos

22
if ([mydict objectForKey:@"mykey"]) {
    // key exists.
}
else
{
    // ...
}

9

Lors de l'utilisation de dictionnaires JSON:

#define isNull(value) value == nil || [value isKindOfClass:[NSNull class]]

if( isNull( dict[@"my_key"] ) )
{
    // do stuff
}

8

J'aime la réponse de Fernandes même si vous demandez l'obj deux fois.

Cela devrait également faire (plus ou moins la même chose que Martin A).

id obj;

if ((obj=[dict objectForKey:@"blah"])) {
   // use obj
} else {
   // Do something else like creating the obj and add the kv pair to the dict
}

Martin et cette réponse fonctionnent tous les deux sur iPad2 iOS 5.0.1 9A405


5

Un piège très méchant qui vient de perdre un peu de mon temps à déboguer - vous pouvez vous retrouver invité par la saisie semi-automatique à essayer d'utiliser doesContaince qui semble fonctionner.

Sauf, doesContainutilise une comparaison d'id au lieu de la comparaison de hachage utilisée par objectForKeydonc si vous avez un dictionnaire avec des clés de chaîne, il retournera NON à a doesContain.

NSMutableDictionary* keysByName = [[NSMutableDictionary alloc] init];
keysByName[@"fred"] = @1;
NSString* test = @"fred";

if ([keysByName objectForKey:test] != nil)
    NSLog(@"\nit works for key lookups");  // OK
else
    NSLog(@"\nsod it");

if (keysByName[test] != nil)
    NSLog(@"\nit works for key lookups using indexed syntax");  // OK
else
    NSLog(@"\nsod it");

if ([keysByName doesContain:@"fred"])
    NSLog(@"\n doesContain works literally");
else
    NSLog(@"\nsod it");  // this one fails because of id comparison used by doesContain

5

Avec Swift, ce serait:

if myDic[KEY] != nil {
    // key exists
}

5

Oui. Ce type d'erreurs est très courant et entraîne un plantage de l'application. J'utilise donc pour ajouter NSDictionary dans chaque projet comme ci-dessous:

//.h code de fichier:

@interface NSDictionary (AppDictionary)

- (id)objectForKeyNotNull : (id)key;

@end

Le code de fichier //.m est comme ci-dessous

#import "NSDictionary+WKDictionary.h"

@implementation NSDictionary (WKDictionary)

 - (id)objectForKeyNotNull:(id)key {

    id object = [self objectForKey:key];
    if (object == [NSNull null])
     return nil;

    return object;
 }

@end

Dans le code, vous pouvez utiliser comme ci-dessous:

NSStrting *testString = [dict objectForKeyNotNull:@"blah"];

3

Pour vérifier l'existence d'une clé dans NSDictionary:

if([dictionary objectForKey:@"Replace your key here"] != nil)
    NSLog(@"Key Exists");
else
    NSLog(@"Key not Exists");

1
Ce n'est vraiment qu'une répétition de cette réponse existante .
Pang

3

Parce que rien ne peut être stocké dans les structures de données Foundation, NSNullc'est parfois pour représenter a nil. Parce qu'il NSNulls'agit d'un objet singleton, vous pouvez vérifier si NSNullla valeur est stockée dans le dictionnaire à l'aide de la comparaison directe de pointeurs:

if ((NSNull *)[user objectForKey:@"myKey"] == [NSNull null]) { }

1
N'a pas fonctionné lorsque je l'ai utilisé avec NSMutableArray comme objet pour ma clé.
pa12

4
Pourquoi diable cela a-t-il été voté? C'est tout simplement faux. Cette vérification retournera vrai si l' NSNullinstance singleton a été stockée dans le dictionnaire comme valeur pour la clé @"myKey". C'est une chose totalement différente que la clé @"myKey"ne soit pas dans le dictionnaire - en effet, les deux s'excluent mutuellement.
Mark Amery

Puisque cela a encore cinq votes tout en étant totalement faux, je ne peux que répéter ce que Mark dit: Ce code teste si le dictionnaire contient une clé avec une valeur nulle, ce qui est totalement différent de ne pas contenir la clé du tout.
gnasher729

C'est "complètement faux, mais indique des informations intéressantes"
Fattie

Cette réponse a été modifiée pour clarifier ce qu'elle fait (vérifier NSNull dans un dict) et ce qu'elle ne fait pas (vérifier une valeur dans un dict)
Andrew Hoos

2

Solution pour Swift 4.2

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
}

1

Je vous suggère de stocker le résultat de la recherche dans une variable temporaire, de tester si la variable temporaire est nulle et de l'utiliser ensuite. De cette façon, vous ne regardez pas le même objet deux fois:

id obj = [dict objectForKey:@"blah"];

if (obj) {
   // use obj
} else {
   // Do something else
}

1

Comme Adirael a suggéré objectForKeyde vérifier l'existence des clés, mais lorsque vous appelez objectForKeydans un dictionnaire annulable, l'application est bloquée, j'ai donc corrigé cela de la manière suivante.

- (instancetype)initWithDictionary:(NSDictionary*)dictionary {
id object = dictionary;

if (dictionary && (object != [NSNull null])) {
    self.name = [dictionary objectForKey:@"name"];
    self.age = [dictionary objectForKey:@"age"];
}
return self;

}


En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.