Jak sprawdzić, czy NSDictionary lub NSMutableDictionary zawiera klucz?

456

Muszę sprawdzić, czy dykt ma klucz, czy nie. W jaki sposób?

dontWatchMyProfile
źródło
4
(Tylko dla każdego, kto się tu przeszukuje . Zauważ, że jest to bardzo stare pytanie . Poniżej odpowiedź SaintMacintosh to najnowszy stan techniki, to pięć lat przed tym QA. Mam nadzieję, że to pomoże.)
Fattie
1
W rzeczywistości odpowiedź Andy'ego Denta daje także najnowocześniejszy obraz i więcej kontekstu. I zrobiło to wcześniej niż SaintMacintosh. Zrób sobie przysługę i przewiń trochę w dół.
Peter Kämpf
1
Używa: keysByName [test]! = Zero! = Zero sprawdzenie jest zbędne, a IMHO mniej czytelny. Chciałem tylko udostępnić wersję TL; DR osobom, które szukają składni.
Andrew Hoos
Zgadzam się z @SaintMacintosh. jego odpowiedź jest o wiele bardziej zwięzła.
Steve Schwarcz
Jeśli chcesz sprawdzić, czy NSDictionaryzawiera żadnego klucza (niespecyficzna) należy użyć [dictionary allKeys].count == 0Jeśli countto 0nie istnieją żadne klucze w NSDictionary.
Aleksander Azizi,

Odpowiedzi:

768

objectForKey zwróci zero, jeśli klucz nie istnieje.

Adirael
źródło
29
A co jeśli klucz istnieje, ale jego odpowiednia wartość wynosi zero?
Fiodor Soikin
232
To nie jest możliwe. Nie można dodać zero do NSDictionary. Musisz użyć [NSNull null]zamiast tego.
Ole Begemann
10
@fyodor nsdictionay zgłosi wyjątek NSInvalidArgumentException, jeśli spróbujesz wstawić wartość zero, więc nigdy nie powinno być przypadku, w którym istnieje klucz, ale odpowiadająca mu wartość wynosi zero.
Brad The App Guy
3
Nie chcesz używać valueForKey, ponieważ w razie potrzeby wywołałoby to objectForKey?
Raffi Khatchadourian
6
Absolutnie NIGDY NIGDY nie chcesz używać valueForKey dla słownika JSON. To dlatego, że JSON może zawierać dowolny ciąg jako klucz. Na przykład, jeśli zawiera on „@count” jako klucz, wówczas objectForKey: @ „@ count” poda poprawną wartość, ale wartośćForKey: @ „@ count” poda liczbę par klucz / wartość.
gnasher729
162
if ([[dictionary allKeys] containsObject:key]) {
    // contains key
}

lub

if ([dictionary objectForKey:key]) {
    // contains object
}
Aleksejs Mjaliks
źródło
100
Przykład pierwszy w tej odpowiedzi jest powolny.
James Van Boxtel
5
czytaj: przykład jeden w tej odpowiedzi należy uznać za nielegalny (O (n) zamiast O (1))
Hertzel Guinness
5
wydajność jest bardzo istotna. Może być prawdą, że ta dokładna linia jest nieistotna, ale jeśli powtórzy się n razy, to nagle, zamiast zajmować n czasu, zajmuje n * mi nie możesz zrozumieć, dlaczego twój program jest powolny. Prawie wszystko jest szybkie raz lub w małych przypadkach. Ale w miarę rozwoju programów przy użyciu niewłaściwej struktury danych (w pierwszym przykładzie tablica zamiast dykta) będzie Cię to kosztować.
Andrew Hoos
2
Sprawdzanie słownika (lub zestawu) pod kątem istnienia klucza jest oczekiwane O (1) z najgorszym przypadkiem O (n). Nie O (log (n)). Dokumentacja Apple jasno to wyjaśnia.
Andrew Hoos,
@JoeBlow „animuj kropki Mecanim 3D” Brzmi to tak, jakbyś mylił Cocoa Touch / Objective-C z Unity Engine / C #. To prawda, że ​​Unity Engine jest strasznie nieefektywny na platformach, na których działa. Jednak wcale nie jest prawdą, że aplikacje Objective-C / Swift są z natury nieefektywne, ani że większość doświadczonych programistów iOS nie jest aktywnie świadoma wydajności swojego kodu. Jeśli chodzi o „dotyczy tylko (4 żyjących) programistów wydajności” - najwyraźniej nie jesteś programistą gier. Świetna grafika i rozgrywka przy 60 klatkach na sekundę bez zabijania baterii to ciągłe wyzwanie.
Slipp D. Thompson,
98

Nowsze wersje Objective-C i Clang mają do tego nowoczesną składnię:

if (myDictionary[myKey]) {

}

Nie musisz sprawdzać równości z wartością zero, ponieważ tylko słowniki Objective-C inne niż zero mogą być przechowywane w słownikach (lub tablicach). Wszystkie obiekty Objective-C są prawdziwymi wartościami. Nawet @NO, @0i [NSNull null]ocenić, jak prawdziwe.

Edycja: Swift jest teraz rzeczą.

W przypadku Swift wypróbujesz coś takiego

if let value = myDictionary[myKey] {

}

Ta składnia wykona blok if tylko wtedy, gdy myKey znajduje się w dykcie, a jeśli tak, to wartość jest przechowywana w zmiennej wartości. Zauważ, że działa to nawet dla wartości falsey, takich jak 0.

Andrew Hoos
źródło
Dzięki za to! Jestem po prostu ciekawy, ale nie mogę znaleźć tego zachowania udokumentowanego w referencji klasy c- developer developer.apple.com/library/mac/documentation/Cocoa/Reference/... Czy jestem po prostu ślepy?
irh
2
Nie, nie jesteś ślepy. Nie jest podkreślone. Ale widać tutaj (szczęk) i tutaj (nowoczesny objc bardzo dolny) i tutaj (zbiory kierować: Dictionary Search)
Andrew Hoos
22
if ([mydict objectForKey:@"mykey"]) {
    // key exists.
}
else
{
    // ...
}
ChristopheD
źródło
9

Podczas korzystania ze słowników JSON:

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

if( isNull( dict[@"my_key"] ) )
{
    // do stuff
}
Ratata Tata
źródło
8

Podoba mi się odpowiedź Fernandesa, mimo że dwukrotnie pytasz o obiekt.

Powinno to również zrobić (mniej więcej to samo, co 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 i ta odpowiedź działają zarówno na iPadzie2 iOS 5.0.1 9A405

q231950
źródło
5

Jedna bardzo nieprzyjemna gotcha, która zmarnowała trochę mojego czasu na debugowanie - może pojawić się monit o automatyczne uzupełnianie, aby spróbować użyć tego, doesContainco wydaje się działać.

Z wyjątkiem tego, że doesContainużywa porównania id zamiast porównania skrótu używanego przezobjectForKey więc jeśli masz słownik z kluczami łańcuchowymi, to zwróci NIE do 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
źródło
5

Używając Swift, byłoby to:

if myDic[KEY] != nil {
    // key exists
}
rsc
źródło
5

Tak. Tego rodzaju błędy są bardzo częste i prowadzą do awarii aplikacji. Używam więc, aby dodać NSDictionary do każdego projektu, jak poniżej:

Kod pliku //.h:

@interface NSDictionary (AppDictionary)

- (id)objectForKeyNotNull : (id)key;

@end

Kod pliku //.m jest jak poniżej

#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

W kodzie możesz użyć, jak poniżej:

NSStrting *testString = [dict objectForKeyNotNull:@"blah"];
rapować
źródło
3

Aby sprawdzić istnienie klucza w NSDictionary:

if([dictionary objectForKey:@"Replace your key here"] != nil)
    NSLog(@"Key Exists");
else
    NSLog(@"Key not Exists");
Aamir
źródło
1
To naprawdę tylko powtórzenie tej istniejącej odpowiedzi .
Pang
3

Ponieważ zero nie może być przechowywane w strukturach danych Fundacji, NSNullczasami ma reprezentować nil. Ponieważ NSNulljest to obiekt singletonowy, możesz sprawdzić, czy NSNullwartość jest przechowywana w słowniku, używając bezpośredniego porównania wskaźnika:

if ((NSNull *)[user objectForKey:@"myKey"] == [NSNull null]) { }
Fernandes
źródło
1
Nie zadziałało, gdy użyłem go z NSMutableArray jako obiektem dla mojego klucza.
pa12,
4
Dlaczego, u licha, zostało to upomniane? To po prostu źle. To sprawdzenie zwróci wartość true, jeśli NSNullinstancja singletonu została zapisana w słowniku jako wartość klucza @"myKey". To zupełnie inna sprawa niż klucz, którego @"myKey"nie ma w słowniku - w istocie oba się wykluczają.
Mark Amery
Ponieważ nadal ma to pięć głosów, choć całkowicie się myliłem, mogę tylko powtórzyć to, co mówi Mark: ten kod sprawdza, czy słownik zawiera klucz o wartości zerowej, co jest zupełnie inne niż brak klucza w ogóle.
gnasher729
Jest to „całkowicie błędne, ale wskazuje na interesujące informacje”
Fattie
Ta odpowiedź została zredagowana, aby wyjaśnić, co robi (sprawdza NSNull w dykcie), a czego nie robi (sprawdza wartość w dykcie)
Andrew Hoos
2

Rozwiązanie dla szybkiego 4.2

Jeśli więc chcesz tylko odpowiedzieć na pytanie, czy słownik zawiera klucz, zapytaj:

let keyExists = dict[key] != nil

Jeśli chcesz tę wartość i wiesz, że słownik zawiera klucz, powiedz:

let val = dict[key]!

Ale jeśli, jak to zwykle bywa, nie wiesz, że zawiera on klucz - chcesz go pobrać i użyć, ale tylko jeśli istnieje - użyj czegoś takiego if let:

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

Sugeruję zapisanie wyniku wyszukiwania w zmiennej temp, przetestowanie, czy zmienna temp jest zerowa, a następnie użycie jej. W ten sposób nie spojrzysz dwukrotnie na ten sam obiekt:

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

if (obj) {
   // use obj
} else {
   // Do something else
}
Jaskółka oknówka
źródło
1

Jak Adirael zasugerował, objectForKeyaby sprawdzić kluczową egzystencję, ale kiedy wywołujesz objectForKeysłownik zerowy, aplikacja ulega awarii, więc naprawiłem to w następujący sposób.

- (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
źródło