¿Cómo verificar si un NSDictionary o NSMutableDictionary contiene una clave?

456

Necesito verificar si un dict tiene una clave o no. ¿Cómo?

dontWatchMyProfile
fuente
44
(. Sólo buscar en Google para cualquier persona que aquí Tenga en cuenta que esta es una muy vieja pregunta . SaintMacintosh respuesta a continuación es el estado de la técnica, es cinco años antes de este control de calidad espero que ayude..)
Fattie
1
En realidad, la respuesta de Andy Dent también da el estado del arte y más contexto. Y lo hizo antes que SaintMacintosh. Hazte un favor un desplazamiento hacia abajo un poco más.
Peter Kämpf
1
Él usa: keysByName [test]! = Nil the! = Nil check es redundante y en mi humilde opinión menos legible. Solo quería compartir la versión TL; DR para las personas que buscan la sintaxis.
Andrew Hoos
Estoy de acuerdo con @SaintMacintosh. Su respuesta es mucho más sucinta.
Steve Schwarcz
Si desea comprobar si la NSDictionarycontiene cualquier tecla (no específica) se debe utilizar [dictionary allKeys].count == 0si el countes 0que no hay llaves en el NSDictionary.
Aleksander Azizi

Respuestas:

768

objectForKey devolverá nil si no existe una clave.

Adirael
fuente
29
¿Y qué pasa si la clave existe, pero su valor correspondiente es nulo?
Fyodor Soikin,
232
Eso no es posible. No puede agregar nil a un NSDictionary. Tendría que usar [NSNull null]en su lugar.
Ole Begemann
10
@fyodor an nsdictionay arrojará una NSInvalidArgumentException si intenta insertar un valor nulo, por lo que nunca debería haber un caso en el que exista una clave, pero el valor correspondiente es nulo.
Brad The App Guy
3
¿No quieres usar valueForKey, ya que eso llamaría a objectForKey si es necesario?
Raffi Khatchadourian
66
Absolutamente NUNCA NUNCA desea utilizar valueForKey para un diccionario JSON. Eso es porque JSON puede contener cualquier cadena como clave. Por ejemplo, si contiene "@count" como clave, entonces objectForKey: @ "@ count" dará el valor correcto, pero valueForKey: @ "@ count" dará el número de pares clave / valor.
gnasher729
162
if ([[dictionary allKeys] containsObject:key]) {
    // contains key
}

o

if ([dictionary objectForKey:key]) {
    // contains object
}
Aleksejs Mjaliks
fuente
100
El ejemplo uno en esta respuesta es lento.
James Van Boxtel
55
lea: el ejemplo uno en esta respuesta debería considerarse ilegal (O (n) en lugar de O (1))
Hertzel Guinness
55
El rendimiento es muy relevante. Puede ser muy cierto que esta línea exacta es irrelevante, pero si va a repetirse n veces, de repente, en lugar de tomarse un tiempo, tomará n * my no podrá descubrir por qué su programa es lento. Casi todo es rápido una vez o en casos pequeños. Pero a medida que los programas crecen usando la estructura de datos incorrecta (como matriz en lugar de dict en el primer ejemplo) terminará costándole mucho.
Andrew Hoos
2
La comprobación de la existencia de una clave en un diccionario (o conjunto) se espera O (1) con el peor caso de O (n). No O (log (n)). La documentación de Apple explica claramente esto.
Andrew Hoos
@JoeBlow "anima los puntos Mecanim 3D" Parece que estás confundiendo Cocoa Touch / Objective-C con Unity Engine / C #. Es cierto que Unity Engine es terriblemente ineficiente en las plataformas en las que se ejecuta. Sin embargo, no es del todo cierto que las aplicaciones Objective-C / Swift sean inherentemente ineficientes, ni que los desarrolladores de iOS más experimentados no sean conscientes de la eficacia de su código. En cuanto a "solo relevante para los programadores de rendimiento (4 vivos)" , claramente no eres un desarrollador de juegos. Excelentes gráficos y jugabilidad a 60FPS sin matar la batería es un desafío continuo.
Slipp D. Thompson
98

Las versiones más recientes de Objective-C y Clang tienen una sintaxis moderna para esto:

if (myDictionary[myKey]) {

}

No es necesario verificar la igualdad con nulo, porque solo los objetos Objective-C no nulos pueden almacenarse en diccionarios (o matrices). Y todos los objetos Objective-C son valores verdaderos. Incluso @NO, @0y [NSNull null]evaluar como verdadero.

Editar: Swift ahora es una cosa.

Para Swift, probarías algo como lo siguiente

if let value = myDictionary[myKey] {

}

Esta sintaxis solo ejecutará el bloque if si myKey está en el dict y si lo está, entonces el valor se almacena en la variable de valor. Tenga en cuenta que esto funciona incluso para valores falsey como 0.

Andrew Hoos
fuente
¡Gracias por esto! Solo tengo curiosidad, pero no puedo encontrar este comportamiento documentado en la referencia de clase objetiva-c developer.apple.com/library/mac/documentation/Cocoa/Reference/… ¿ Estoy ciego?
irh
2
No, no eres ciego. No se enfatiza. Pero visible aquí (clang) y aquí (objc moderno: muy abajo) y aquí (guía de colecciones: diccionario de búsqueda)
Andrew Hoos
22
if ([mydict objectForKey:@"mykey"]) {
    // key exists.
}
else
{
    // ...
}
ChristopheD
fuente
9

Al usar diccionarios JSON:

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

if( isNull( dict[@"my_key"] ) )
{
    // do stuff
}
Ratata Tata
fuente
8

Me gusta la respuesta de Fernandes aunque pidas el obj dos veces.

Esto también debería hacer (más o menos lo mismo que Martin's 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 y esta respuesta funcionan en iPad2 iOS 5.0.1 9A405

q231950
fuente
5

Un problema muy desagradable que acaba de perder un poco de mi tiempo de depuración: es posible que el autocompletado te pida que intentes usar lo doesContainque parece funcionar.

Excepto, doesContainutiliza una comparación de id en lugar de la comparación de hash utilizada por objectForKeylo que si tiene un diccionario con teclas de cadena, devolverá NO a 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
fuente
5

Usando Swift, sería:

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

Si. Este tipo de errores son muy comunes y conducen al bloqueo de la aplicación. Entonces uso para agregar NSDictionary en cada proyecto de la siguiente manera:

//.h código de archivo:

@interface NSDictionary (AppDictionary)

- (id)objectForKeyNotNull : (id)key;

@end

//.m el código del archivo es el siguiente

#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

En el código puede usar lo siguiente:

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

Para verificar la existencia de la clave en NSDictionary:

if([dictionary objectForKey:@"Replace your key here"] != nil)
    NSLog(@"Key Exists");
else
    NSLog(@"Key not Exists");
Aamir
fuente
1
Esto es realmente solo una repetición de esta respuesta existente .
Pang
3

Debido a que nil no se puede almacenar en las estructuras de datos de Foundation, a NSNullveces se representa unnil . Debido a que NSNulles un objeto singleton, puede verificar si NSNulles el valor almacenado en el diccionario mediante la comparación de puntero directo:

if ((NSNull *)[user objectForKey:@"myKey"] == [NSNull null]) { }
Fernandes
fuente
1
No funcionó cuando lo usé con NSMutableArray como objeto para mi clave.
pa12
44
¿Por qué demonios se votó esto? Simplemente está mal. Esta comprobación devolverá verdadero si la NSNullinstancia de singleton se ha almacenado en el diccionario como el valor de la clave @"myKey". Eso es algo totalmente diferente a que la clave @"myKey"no esté en el diccionario; de hecho, los dos son mutuamente excluyentes.
Mark Amery
Como todavía tiene cinco votos y está totalmente equivocado, solo puedo repetir lo que dice Mark: este código prueba si el diccionario contiene una clave con un valor nulo, que es totalmente diferente de no contener la clave.
gnasher729
Está "completamente equivocado, pero apunta a información interesante"
Fattie
Esta respuesta ha sido editada para aclarar lo que hace (verifica NSNull en un dict) y lo que no hace (verifica un valor en un dict)
Andrew Hoos
2

Solución para swift 4.2

Entonces, si solo quiere responder a la pregunta de si el diccionario contiene la clave, pregunte:

let keyExists = dict[key] != nil

Si desea el valor y sabe que el diccionario contiene la clave, diga:

let val = dict[key]!

Pero si, como suele suceder, no sabe que contiene la clave, desea buscarla y usarla, pero solo si existe, use algo como if let:

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

Le sugiero que almacene el resultado de la búsqueda en una variable temporal, pruebe si la variable temporal es nula y luego úsela. De esa manera no miras el mismo objeto dos veces:

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

if (obj) {
   // use obj
} else {
   // Do something else
}
Martín
fuente
1

Como Adirael sugirió objectForKeyverificar la existencia de la clave, pero cuando llamas objectForKeyal diccionario anulable, la aplicación se bloquea, así que lo solucioné de la siguiente manera.

- (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
fuente
0
if ([MyDictionary objectForKey:MyKey]) {
      // "Key Exist"
} 
saurabh rathod
fuente
Esto es realmente solo una repetición de esta respuesta existente .
Pang