Wie überprüfe ich, ob ein NSDictionary oder NSMutableDictionary einen Schlüssel enthält?

456

Ich muss überprüfen, ob ein Diktat einen Schlüssel hat oder nicht. Wie?

dontWatchMyProfile
quelle
4
(Nur für alle, die hierher googeln. Beachten Sie, dass dies eine sehr alte Frage ist . Die Antwort von SaintMacintosh unten ist der Stand der Technik. Sie ist dieser Qualitätssicherung fünf Jahre voraus. Ich hoffe, es hilft.)
Fattie
1
Tatsächlich gibt Andy Dents Antwort auch den Stand der Technik und mehr Kontext. Und das früher als SaintMacintosh. Tun Sie sich selbst einen Gefallen und scrollen Sie etwas weiter nach unten.
Peter Kämpf
1
Er benutzt: keysByName [test]! = Nil die! = Nil-Prüfung ist redundant und meiner Meinung nach weniger lesbar. Ich wollte nur die TL; DR-Version für Leute teilen, die die Syntax suchen.
Andrew Hoos
Ich stimme @SaintMacintosh zu. seine Antwort ist viel prägnanter.
Steve Schwarcz
Wenn Sie überprüfen wollen , ob die NSDictionarybeliebige Taste (unspezifisch) enthält , sollten Sie verwenden , [dictionary allKeys].count == 0wenn die countist 0es keine Tasten in der sind NSDictionary.
Aleksander Azizi

Antworten:

768

objectForKey gibt nil zurück, wenn kein Schlüssel existiert.

Adirael
quelle
29
Und was ist, wenn der Schlüssel existiert, der entsprechende Wert jedoch Null ist?
Fjodor Soikin
232
Das ist doch nicht möglich. Sie können einem NSDictionary keine Null hinzufügen. Sie müssten [NSNull null]stattdessen verwenden.
Ole Begemann
10
@fyodor Ein nsdictionay löst eine NSInvalidArgumentException aus, wenn Sie versuchen, einen Nullwert einzufügen. Daher sollte es niemals einen Fall geben, in dem ein Schlüssel vorhanden ist, der entsprechende Wert jedoch Null ist.
Brad The App Guy
3
Möchten Sie valueForKey nicht verwenden, da dies bei Bedarf objectForKey aufrufen würde?
Raffi Khatchadourian
6
Sie möchten valueForKey NIEMALS für ein JSON-Wörterbuch verwenden. Dies liegt daran, dass JSON eine beliebige Zeichenfolge als Schlüssel enthalten kann . Wenn es beispielsweise "@count" als Schlüssel enthält, gibt objectForKey: @ "@ count" den korrekten Wert an, aber valueForKey: @ "@ count" gibt die Anzahl der Schlüssel / Wert-Paare an.
Gnasher729
162
if ([[dictionary allKeys] containsObject:key]) {
    // contains key
}

oder

if ([dictionary objectForKey:key]) {
    // contains object
}
Aleksejs Mjaliks
quelle
100
Beispiel eins in dieser Antwort ist langsam.
James Van Boxtel
5
Lesen Sie: Beispiel eins in dieser Antwort sollte als illegal angesehen werden (O (n) anstelle von O (1))
Hertzel Guinness
5
Leistung ist sehr relevant. Es mag sehr wahr sein, dass diese genaue Zeile irrelevant ist, aber wenn sie n-mal wiederholt wird, dauert es plötzlich n * m, anstatt n-mal zu dauern, und Sie können nicht herausfinden, warum Ihr Programm langsam ist. Fast alles ist einmal oder in kleinen Fällen schnell. Wenn Programme jedoch mit der falschen Datenstruktur wachsen (wie Array anstelle von Diktat im ersten Beispiel), werden Sie viel Geld kosten.
Andrew Hoos
2
Das Überprüfen eines Wörterbuchs (oder einer Menge) auf das Vorhandensein eines Schlüssels wird mit O (1) und einem Worst-Case von O (n) erwartet. Nicht O (log (n)). Die Dokumentation von Apple erklärt dies deutlich.
Andrew Hoos
@JoeBlow "animiere die 3D-Mecanim-Punkte" Es klingt so, als würdest du Cocoa Touch / Objective-C mit Unity Engine / C # verwechseln. Es ist wahr, dass die Unity Engine auf den Plattformen, auf denen sie ausgeführt wird, furchtbar ineffizient ist. Es ist jedoch keineswegs richtig, dass Objective-C / Swift-Anwendungen von Natur aus ineffizient sind oder dass die meisten erfahrenen iOS-Entwickler die Effizienz ihres Codes nicht aktiv kennen. Was "nur für die (4 lebenden) Performance-Programmierer relevant" betrifft, sind Sie eindeutig kein Spieleentwickler. Hervorragende Grafik und Gameplay bei 60 FPS ohne Batterieentladung sind eine ständige Herausforderung.
Slipp D. Thompson
98

Neuere Versionen von Objective-C und Clang haben hierfür eine moderne Syntax:

if (myDictionary[myKey]) {

}

Sie müssen nicht auf Gleichheit mit nil prüfen, da nur Objective-C-Objekte ungleich nil in Wörterbüchern (oder Arrays) gespeichert werden können. Und alle Objective-C-Objekte sind wahrheitsgemäße Werte. Auch @NO, @0und [NSNull null]bewertet , wie wahr.

Edit: Swift ist jetzt eine Sache.

Für Swift würden Sie so etwas wie das Folgende versuchen

if let value = myDictionary[myKey] {

}

Diese Syntax führt den if-Block nur aus, wenn sich myKey im Diktat befindet und wenn dies der Fall ist, wird der Wert in der Wertvariablen gespeichert. Beachten Sie, dass dies auch für Falsey-Werte wie 0 funktioniert.

Andrew Hoos
quelle
Danke dafür! Ich bin nur neugierig, aber ich kann dieses Verhalten nicht in der Referenz der Ziel-C-Klasse finden. Developer.apple.com/library/mac/documentation/Cocoa/Reference/… Bin ich nur blind?
irh
2
Nein, du bist nicht blind. Es wird nicht betont. Aber sichtbar hier (klirrend) und hier (modernes Objekt: ganz unten) und hier (Sammlungsführer: Suchwörterbuch)
Andrew Hoos
22
if ([mydict objectForKey:@"mykey"]) {
    // key exists.
}
else
{
    // ...
}
ChristopheD
quelle
9

Bei Verwendung von JSON-Wörterbüchern:

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

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

Ich mag Fernandes 'Antwort, obwohl Sie zweimal nach dem Objekt fragen.

Dies sollte auch funktionieren (mehr oder weniger dasselbe wie Martins 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
}

Martins und diese Antwort funktionieren beide auf dem iPad2 iOS 5.0.1 9A405

q231950
quelle
5

Ein sehr böses Problem, das nur ein wenig Zeit mit dem Debuggen verschwendet hat - Sie werden möglicherweise durch die automatische Vervollständigung aufgefordert, zu versuchen, doesContaindas zu verwenden, was zu funktionieren scheint.

Ausgenommen, doesContainverwendet einen ID-Vergleich anstelle des von verwendeten Hash-Vergleichs. objectForKeyWenn Sie also ein Wörterbuch mit Zeichenfolgenschlüsseln haben, wird NO an a zurückgegeben 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
quelle
5

Mit Swift wäre es:

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

Ja. Diese Art von Fehlern ist sehr häufig und führt zum Absturz der App. Also füge ich NSDictionary in jedem Projekt wie folgt hinzu:

//.h Dateicode:

@interface NSDictionary (AppDictionary)

- (id)objectForKeyNotNull : (id)key;

@end

//.m Dateicode ist wie folgt

#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

Im Code können Sie wie folgt verwenden:

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

So überprüfen Sie das Vorhandensein eines Schlüssels in NSDictionary:

if([dictionary objectForKey:@"Replace your key here"] != nil)
    NSLog(@"Key Exists");
else
    NSLog(@"Key not Exists");
Aamir
quelle
1
Dies ist wirklich nur eine Wiederholung dieser bestehenden Antwort .
Pang
3

Da nil nicht in Foundation-Datenstrukturen gespeichert werden kann, muss NSNullmanchmal a dargestellt werden nil. Da NSNulles sich um ein Singleton-Objekt handelt, können Sie NSNullmithilfe des direkten Zeigervergleichs überprüfen, ob der Wert im Wörterbuch gespeichert ist:

if ((NSNull *)[user objectForKey:@"myKey"] == [NSNull null]) { }
Fernandes
quelle
1
Hat nicht geklappt, als ich es mit NSMutableArray als Objekt für meinen Schlüssel verwendet habe.
pa12
4
Warum um alles in der Welt wurde dies positiv bewertet? Es ist einfach falsch. Diese Prüfung gibt true zurück, wenn die Singleton- NSNullInstanz als Wert für den Schlüssel im Wörterbuch gespeichert wurde @"myKey". Das ist eine ganz andere Sache als der Schlüssel, der @"myKey"nicht im Wörterbuch enthalten ist - tatsächlich schließen sich die beiden gegenseitig aus.
Mark Amery
Da dies immer noch fünf Stimmen hat, obwohl es völlig falsch ist, kann ich nur wiederholen, was Mark sagt: Dieser Code testet, ob das Wörterbuch einen Schlüssel mit einem Nullwert enthält, der sich völlig davon unterscheidet, den Schlüssel überhaupt nicht zu enthalten.
Gnasher729
Es ist "völlig falsch, zeigt aber auf interessante Informationen"
Fattie
Diese Antwort wurde bearbeitet, um zu klären, was es tut (in einem Diktat auf NSNull prüfen) und was nicht (auf einen Wert in einem Diktat prüfen)
Andrew Hoos
2

Lösung für schnelle 4.2

Wenn Sie also nur die Frage beantworten möchten, ob das Wörterbuch den Schlüssel enthält, fragen Sie:

let keyExists = dict[key] != nil

Wenn Sie den Wert möchten und wissen, dass das Wörterbuch den Schlüssel enthält, sagen Sie:

let val = dict[key]!

Wenn Sie jedoch wie gewöhnlich nicht wissen, dass der Schlüssel enthalten ist - Sie möchten ihn abrufen und verwenden, aber nur, wenn er vorhanden ist -, verwenden Sie Folgendes if let:

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

Ich würde vorschlagen, dass Sie das Ergebnis der Suche in einer temporären Variablen speichern, testen, ob die temporäre Variable Null ist, und sie dann verwenden. Auf diese Weise suchen Sie nicht zweimal dasselbe Objekt nach:

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

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

Wie Adirael vorgeschlagen hat objectForKey, die Schlüsselexistenz zu überprüfen, aber wenn Sie objectForKeyein nullbares Wörterbuch aufrufen , stürzt die App ab, sodass ich dies auf folgende Weise behoben habe.

- (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
quelle