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?

dontWatchMyProfile
la source
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.

Adirael
la source
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
}
Aleksejs Mjaliks
la source
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.

Andrew Hoos
la source
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
{
    // ...
}
ChristopheD
la source
9

Lors de l'utilisation de dictionnaires JSON:

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

if( isNull( dict[@"my_key"] ) )
{
    // do stuff
}
Ratata Tata
la source
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

q231950
la source
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
Andy Dent
la source
5

Avec Swift, ce serait:

if myDic[KEY] != nil {
    // key exists
}
rsc
la source
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"];
au rap
la source
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");
Aamir
la source
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]) { }
Fernandes
la source
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
}
Enea Dume
la source
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
}
Martin
la source
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;

}

Aleem
la source