Como verificar se um NSDictionary ou NSMutableDictionary contém uma chave?

456

Preciso verificar se um ditado tem uma chave ou não. Quão?

dontWatchMyProfile
fonte
4
(. Apenas para quem pesquisando para aqui Note-se que esta é uma questão de idade . SaintMacintosh resposta abaixo é o estado da arte, que é de cinco anos à frente deste QA Espero que ajude..)
Fattie
1
Na verdade, a resposta de Andy Dent também fornece o estado da arte e mais contexto. E fez isso antes do SaintMacintosh. Faça um favor a si mesmo e role um pouco mais.
Peter Kämpf
1
Ele usa: keysByName [test]! = Nil a verificação! = Nil é redundante e IMHO menos legível. Eu só queria compartilhar a versão TL; DR para pessoas que procuram a sintaxe.
Andrew Hoos 30/07
Eu concordo com @SaintMacintosh. sua resposta é muito mais sucinta.
21320 Steve Jobs
Se você deseja verificar se o NSDictionarycontém qualquer (não-específica) chave que você deve usar [dictionary allKeys].count == 0Se o counté 0não há chaves no NSDictionary.
Aleksander Azizi

Respostas:

768

objectForKey retornará nulo se uma chave não existir.

Adirael
fonte
29
E se a chave existir, mas o valor correspondente for nulo?
Fyodor Soikin
232
Isso não é possível. Você não pode adicionar nada a um NSDictionary. Você teria que usar em seu [NSNull null]lugar.
Ole Begemann
10
@fyodor um nsdictionay lançará uma NSInvalidArgumentException se você tentar inserir um valor nulo, portanto nunca deve haver um caso em que exista uma chave, mas o valor correspondente é nulo.
Brad The App Guy
3
Você não deseja usar valueForKey, pois isso chamaria objectForKey, se necessário?
Raffi Khatchadourian
6
Você NUNCA NUNCA deseja usar valueForKey para um dicionário JSON. Isso ocorre porque o JSON pode conter qualquer sequência como chave. Por exemplo, se ele contiver "@count" como uma chave, objectForKey: @ "@ count" fornecerá o valor correto, mas valueForKey: @ "@ count" fornecerá o número de pares chave / valor.
precisa saber é o seguinte
162
if ([[dictionary allKeys] containsObject:key]) {
    // contains key
}

ou

if ([dictionary objectForKey:key]) {
    // contains object
}
Aleksejs Mjaliks
fonte
100
O exemplo um desta resposta é lento.
James Van Boxtel
5
li: um exemplo desta resposta deve ser considerado ilegal (S (n), em vez de O (1))
Hertzel Guinness
5
o desempenho é muito relevante. Pode ser bem verdade que essa linha exata é irrelevante, mas se for repetida n vezes, de repente, em vez de demorar n, está tomando n * me você não consegue entender por que seu programa é lento. Quase tudo é rápido uma vez ou em pequenos casos. Porém, à medida que os programas crescem usando a estrutura de dados incorreta (como matriz, em vez de ditado no primeiro exemplo), acabará custando muito.
Andrew Hoos
2
A verificação de um dicionário (ou conjunto) para a existência de uma chave é esperada O (1) com o pior caso de O (n). Não O (log (n)). A documentação da Apple explica claramente isso.
Andrew Hoos
@JoeBlow “animar os pontos 3D Mecanim” Parece que você está confundindo Cocoa Touch / Objective-C com Unity Engine / C #. É verdade que o Unity Engine é terrivelmente ineficiente nas plataformas em que é executado. No entanto, não é de todo verdade que os aplicativos Objective-C / Swift sejam inerentemente ineficientes, nem que a maioria dos desenvolvedores experientes do iOS não tenha conhecimento ativo da eficiência de seu código. Quanto a "apenas relevante para os programadores de desempenho (4 vivos)" - você claramente não é desenvolvedor de jogos. Ótimos gráficos e jogabilidade a 60FPS sem matar a bateria é um desafio contínuo.
Slipp D. Thompson
98

Versões mais recentes do Objective-C e Clang têm uma sintaxe moderna para isso:

if (myDictionary[myKey]) {

}

Você não precisa verificar a igualdade com zero, porque apenas objetos Objective-C diferentes de zero podem ser armazenados em dicionários (ou matrizes). E todos os objetos Objective-C são valores verdadeiros. Mesmo @NO, @0e [NSNull null]avaliar como verdadeiro.

Edit: Swift agora é uma coisa.

Para Swift, você tentaria algo como o seguinte

if let value = myDictionary[myKey] {

}

Essa sintaxe somente executará o bloco if se myKey estiver no comando e se estiver, o valor será armazenado na variável value. Observe que isso funciona para valores até falsey como 0.

Andrew Hoos
fonte
Obrigado por isso! Estou curioso, mas não consigo encontrar esse comportamento documentado na referência da classe objetivo-c developer.apple.com/library/mac/documentation/Cocoa/Reference/… Estou apenas cego?
IRH
2
Não, você não é cego. Não é enfatizado. Mas visível aqui (clang) e aqui (objc moderno: muito baixo) e aqui (coleções guiar: procure dicionário)
Andrew Hoos
22
if ([mydict objectForKey:@"mykey"]) {
    // key exists.
}
else
{
    // ...
}
ChristopheD
fonte
9

Ao usar dicionários JSON:

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

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

Gosto da resposta de Fernandes, mesmo que você peça o objeto duas vezes.

Isso também deve ser feito (mais ou menos o mesmo que o A de 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
}

A resposta de Martin e esta resposta funcionam no iPad2 iOS 5.0.1 9A405

q231950
fonte
5

Uma pegadinha muito desagradável que desperdiçou um pouco do meu tempo depurando - você pode ser solicitado pelo preenchimento automático a tentar usar doesContain que parece funcionar.

Exceto, doesContainusa uma comparação de id em vez da comparação de hash usada por objectForKeyisso, se você tiver um dicionário com chaves de string, ele retornará 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
fonte
5

Usando Swift, seria:

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

Sim. Esse tipo de erro é muito comum e leva à falha do aplicativo. Então, eu uso para adicionar NSDictionary em cada projeto, como abaixo:

//.h código do arquivo:

@interface NSDictionary (AppDictionary)

- (id)objectForKeyNotNull : (id)key;

@end

//.m código do arquivo é como abaixo

#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

No código você pode usar como abaixo:

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

Para verificar a existência da chave no NSDictionary:

if([dictionary objectForKey:@"Replace your key here"] != nil)
    NSLog(@"Key Exists");
else
    NSLog(@"Key not Exists");
Aamir
fonte
1
Esta é realmente apenas uma repetição desta resposta existente .
Pang
3

Como nulo não pode ser armazenado nas estruturas de dados do Foundation, NSNullalgumas vezes, representa a nil. Como NSNullé um objeto singleton, você pode verificar se NSNullé o valor armazenado no dicionário usando a comparação direta de ponteiros:

if ((NSNull *)[user objectForKey:@"myKey"] == [NSNull null]) { }
Fernandes
fonte
1
Não funcionou quando o usei com o NSMutableArray como objeto da minha chave.
pa12 19/12/12
4
Por que diabos isso foi votado? É simplesmente errado. Essa verificação retornará true se a NSNullinstância singleton tiver sido armazenada no dicionário como o valor da chave @"myKey". Isso é uma coisa totalmente diferente da chave @"myKey"não estar no dicionário - na verdade as duas são mutuamente exclusivas.
Mark Amery
Como isso ainda tem cinco votos e está totalmente errado, só posso repetir o que Mark diz: Esse código testa se o dicionário contém uma chave com um valor nulo, que é totalmente diferente de não conter a chave.
precisa saber é o seguinte
É "completamente errado, mas aponta para informações interessantes"
Fattie
Essa resposta foi editado para esclarecer o que ele faz (verificar NSNull em um dicionário) eo que ele não faz (verificação para um valor em um dict)
Andrew Hoos
2

Solução para swift 4.2

Portanto, se você quiser apenas responder à pergunta se o dicionário contém a chave, pergunte:

let keyExists = dict[key] != nil

Se você deseja o valor e sabe que o dicionário contém a chave, diga:

let val = dict[key]!

Mas se, como geralmente acontece, você não sabe que ela contém a chave - você deseja buscá-la e usá-la, mas apenas se ela existir -, 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
fonte
1

Sugiro que você armazene o resultado da pesquisa em uma variável temp, teste se a variável temp é nula e use-a. Dessa forma, você não procura o mesmo objeto duas vezes:

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

if (obj) {
   // use obj
} else {
   // Do something else
}
Martin
fonte
1

Como Adirael sugeriu objectForKeyverificar a existência de chaves, mas quando você chama um objectForKeydicionário anulável, o aplicativo fica travado, então eu o corrigi da seguinte maneira.

- (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
fonte
0
if ([MyDictionary objectForKey:MyKey]) {
      // "Key Exist"
} 
saurabh rathod
fonte
Esta é realmente apenas uma repetição desta resposta existente .
Pang