Come verificare se un NSDictionary o NSMutableDictionary contiene una chiave?


456

Devo verificare se un dict ha una chiave o meno. Come?


4
(Solo per chiunque googling qui. Nota che questa è una domanda molto vecchia . La risposta di SaintMacintosh di seguito è lo stato dell'arte, è cinque anni avanti rispetto a questo QA. Spero che sia d'aiuto.)
Fattie

1
In realtà, la risposta di Andy Dent fornisce anche lo stato dell'arte e più contesto. E lo ha fatto prima di SaintMacintosh. Fatti un favore e scorri ancora un po '.
Peter Kämpf,

1
Usa: keysByName [test]! = Zero il controllo! = Zero è ridondante e IMHO meno leggibile. Volevo solo condividere la versione TL; DR per le persone che cercavano la sintassi.
Andrew Hoos,

Sono d'accordo con @SaintMacintosh. la sua risposta è molto più concisa.
Steve Schwarcz,

Se si desidera controllare se il NSDictionarycontiene un tasto qualsiasi (non specifico) si dovrebbe usare [dictionary allKeys].count == 0Se l' countè 0non ci sono chiavi nel NSDictionary.
Aleksander Azizi,

Risposte:


768

objectForKey restituirà zero se non esiste una chiave.


29
E se la chiave esiste, ma il valore corrispondente è zero?
Fyodor Soikin,

232
Non e possibile. Non è possibile aggiungere nulla a un NSDictionary. Dovresti usare [NSNull null]invece.
Ole Begemann,

10
@fyodor an nsdictionay genererà un NSInvalidArgumentException se si tenta di inserire un valore nullo, quindi non dovrebbe mai esserci un caso in cui esiste una chiave, ma il valore corrispondente è nullo.
Brad The App Guy,

3
Non vuoi usare valueForKey, poiché ciò chiamerebbe objectForKey se necessario?
Raffi Khatchadourian,

6
Non si desidera MAI MAI MAI utilizzare valueForKey per un dizionario JSON. Questo perché JSON può contenere qualsiasi stringa come chiave. Ad esempio, se contiene "@count" come chiave, objectForKey: @ "@ count" fornirà il valore corretto, ma valueForKey: @ "@ count" fornirà il numero di coppie chiave / valore.
gnasher729,

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

o

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

100
L'esempio 1 in questa risposta è lento.
James Van Boxtel,

5
leggi: l'esempio 1 in questa risposta dovrebbe essere considerato illegale (O (n) invece di O (1))
Hertzel Guinness

5
le prestazioni sono molto rilevanti. Può essere vero che questa linea esatta sia irrilevante, ma se verrà ripetuta n volte allora all'improvviso invece di prendere n tempo impiegando n * m e non riesci a capire perché il tuo programma è lento. Quasi tutto è veloce una volta o in piccoli casi. Ma man mano che i programmi crescono usando la struttura di dati errata (come array invece di dict nel primo esempio) finirà per costarti molto.
Andrew Hoos,

2
Il controllo di un dizionario (o set) per l'esistenza di una chiave è previsto O (1) con il caso peggiore di O (n). Non O (log (n)). La documentazione di Apple spiega chiaramente questo.
Andrew Hoos,

@JoeBlow “animare i punti 3D Mecanim” Sembra che tu stia confondendo Cocoa Touch / Objective-C con Unity Engine / C #. È vero che Unity Engine è terribilmente inefficiente sulle piattaforme su cui gira. Tuttavia, non è affatto vero che le applicazioni Objective-C / Swift sono intrinsecamente inefficienti, né che gli sviluppatori iOS più esperti non sono attivamente consapevoli dell'efficienza del loro codice. Per quanto riguarda “rilevante solo per i (4 viventi) programmatori di prestazioni” - chiaramente non sei uno sviluppatore di giochi. Ottima grafica e gameplay a 60FPS senza uccidere la batteria è una sfida continua.
Slipp D. Thompson,

98

Le versioni più recenti di Objective-C e Clang hanno una sintassi moderna per questo:

if (myDictionary[myKey]) {

}

Non è necessario verificare l'uguaglianza con zero, poiché solo gli oggetti Objective-C non nulli possono essere memorizzati in dizionari (o matrici). E tutti gli oggetti Objective-C sono valori di verità. Anche @NO, @0e [NSNull null]valutare come vero.

Modifica: Swift ora è una cosa.

Per Swift proveresti qualcosa di simile al seguente

if let value = myDictionary[myKey] {

}

Questa sintassi eseguirà il blocco if solo se myKey è nel dict e se lo è, il valore viene archiviato nella variabile value. Nota che questo funziona anche con valori falsi come 0.


Grazie per questo! Sono solo curioso, ma non riesco a trovare questo comportamento documentato nella classe di riferimento dell'obiettivo-c developer.apple.com/library/mac/documentation/Cocoa/Reference/… AM Sono solo cieco?
ir

2
No, non sei cieco. Non è sottolineato. Ma visibile qui (clang) e qui (oggetto moderno: molto in basso) e qui (guida alle collezioni: dizionario di ricerca)
Andrew Hoos

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

9

Quando si utilizzano i dizionari JSON:

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

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

8

Mi piace la risposta di Fernandes anche se tu chiedi l'obj due volte.

Questo dovrebbe anche fare (più o meno lo stesso della A di Martin).

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 e questa risposta funzionano entrambi su iPad2 iOS 5.0.1 9A405


5

Un gotcha molto brutto che ha appena perso un po 'del mio tempo nel debug: potresti trovarti spinto dal completamento automatico per provare a usare quello doesContainche sembra funzionare.

Ad eccezione, doesContainutilizza un confronto ID anziché il confronto hash utilizzato da objectForKeyquindi se si dispone di un dizionario con chiavi stringa, restituirà NO 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

Usando Swift, sarebbe:

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

5

Sì. Questo tipo di errori sono molto comuni e portano al crash dell'app. Quindi io uso per aggiungere NSDictionary in ogni progetto come di seguito:

//.h codice file:

@interface NSDictionary (AppDictionary)

- (id)objectForKeyNotNull : (id)key;

@end

Il codice del file //.m è il seguente

#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

Nel codice è possibile utilizzare come di seguito:

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

3

Per verificare l'esistenza della chiave in NSDictionary:

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

1
Questa è davvero solo una ripetizione di questa risposta esistente .
Pang

3

Perché zero non può essere archiviato nelle strutture di dati di Foundation a NSNullvolte significa rappresentare un nil. Poiché NSNullè un oggetto singleton, puoi verificare se NSNullè il valore memorizzato nel dizionario utilizzando il confronto puntatore diretto:

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

1
Non ha funzionato quando l'ho usato con NSMutableArray come oggetto per la mia chiave.
pa12

4
Perché mai questo è stato votato? È semplicemente sbagliato. Questo controllo restituirà true se l' NSNullistanza singleton è stata memorizzata nel dizionario come valore per la chiave @"myKey". È una cosa totalmente diversa dal fatto che la chiave @"myKey"non si trova nel dizionario - in effetti i due si escludono a vicenda.
Mark Amery,

Poiché questo ha ancora cinque voti pur essendo totalmente sbagliato, posso solo ripetere ciò che dice Mark: questo codice verifica se il dizionario contiene una chiave con un valore nullo, che è totalmente diverso dal non contenere affatto la chiave.
gnasher729,

È "completamente sbagliato, ma indica informazioni interessanti"
Fattie,

Questa risposta è stata modificata per chiarire cosa fa (controlla per NSNull in un dict) e cosa non fa (controlla per un valore in un dict)
Andrew Hoos,

2

Soluzione per rapido 4.2

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
}

1

Ti suggerirei di archiviare il risultato della ricerca in una variabile temp, verificare se la variabile temp è nulla e quindi utilizzarla. In questo modo non guardi lo stesso oggetto due volte:

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

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

1

Come Adirael ha suggerito objectForKeydi controllare l'esistenza dei tasti ma quando si chiama objectForKeynel dizionario nullable, l'app si blocca, quindi ho risolto il problema nel modo seguente.

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

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

}


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.