Як перевірити, чи містить ключ NSDictionary або NSMutableDictionary?


456

Мені потрібно перевірити, чи є в дікті ключ чи ні. Як?


4
(Просто для тих, хто тут гуглить . Зауважте, що це дуже давнє запитання . Відповідь SaintMacintosh нижче - це сучасний стан, це п’ять років попереду цього QA. Сподіваюся, що це допомагає.)
Fattie

1
Власне, відповідь Енді Дента також дає сучасний стан та більше контексту. І це було раніше, ніж SaintMacintosh. Зроби собі послугу прокручування вниз трохи більше.
Пітер Кемпф

1
Він використовує: keysByName [тест]! = Нуль!! Нульова перевірка зайва і IMHO менш читабельна. Я просто хотів поділитися версією TL; DR для людей, які шукають синтаксис.
Ендрю Хоос

Я згоден з @SaintMacintosh. його відповідь набагато більш лаконічна.
Стів Шварч

Якщо ви хочете , щоб перевірити, NSDictionaryмістить будь - яких ключ (неспецифічний) дотримуєшся використовувати [dictionary allKeys].count == 0Якщо countце 0немає ключів в NSDictionary.
Олександр Азізі

Відповіді:


768

objectForKey поверне нуль, якщо ключ не існує.


29
А що робити, якщо ключ існує, але його відповідне значення дорівнює нулю?
Федір Сойкін

232
Це неможливо. Ви не можете додати нуль до NSDictionary. Вам доведеться використовувати [NSNull null]замість цього.
Оле Бегеман

10
@fyodor a nsdictionay викине NSInvalidArgumentException, якщо ви спробуєте вставити значення нуля, тому ніколи не повинно бути випадку, коли ключ існує, але відповідне значення - нульове.
Brad The App Guy

3
Ви не хочете використовувати valueForKey, як це за потреби викликатиме objectForKey?
Раффі Хатчадуріан

6
Ви абсолютно НІКОЛИ НІКОЛИ не хочете використовувати valueForKey для словника JSON. Це тому, що JSON може містити будь-який рядок як ключ. Наприклад, якщо він містить "@count" як ключ, то objectForKey: @ "@ count" дасть правильне значення, але значенняForKey: @ "@ count" дасть кількість пар ключів / значень.
gnasher729

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

або

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

100
Приклад перший у цій відповіді повільний.
Джеймс Ван Бокстель

5
читайте: приклад перший у цій відповіді слід вважати незаконним (O (n) замість O (1))
Герцель Гіннес

5
Виступ дуже актуальний. Це може бути дуже правдивим, що ця точна лінія не має значення, але якщо вона повториться n разів, то раптом замість того, щоб вона зайняла n разів, її зайняти n * m, і ви не можете зрозуміти, чому ваша програма повільна. Майже все швидко відбувається один раз або в невеликих випадках. Але в міру того, як програми ростуть за допомогою неправильної структури даних (наприклад, масив замість диктату в першому прикладі), це призведе до великих витрат.
Ендрю Хоос

2
Очікується перевірка словника (або набору) на наявність ключа O (1) з гіршим випадком O (n). Не O (log (n)). Документація Apple чітко пояснює це.
Ендрю Хоос

@JoeBlow " оживіть 3D-точки Mecanim". Це здається, що ви плутаєте Cocoa Touch / Objective-C з Unity Engine / C #. Це правда, що Unity Engine надзвичайно неефективний на платформах, на яких він працює. Однак це зовсім неправда, що програми Objective-C / Swift за своєю суттю неефективні, а також те, що більшість досвідчених розробників iOS активно не усвідомлюють ефективності свого коду. Що стосується "релевантних лише для (4 живих) програмістів продуктивності" - ви, очевидно, не розробник ігор. Чудова графіка та геймплей при 60 FPS без вбивства акумулятора - це постійне завдання.
Сліп Д. Томпсон

98

Більш новітні версії Objective-C і Clang мають для цього сучасний синтаксис:

if (myDictionary[myKey]) {

}

Не потрібно перевіряти рівність з нулем, оскільки в словниках (або масивах) можуть зберігатися лише ненульові об'єкти Objective-C. І всі об'єкти Objective-C є трибунними значеннями. Навіть @NO, @0і [NSNull null]оцініть як істинне.

Редагувати: Свіфт зараз річ.

Для Swift ви б спробували щось подібне

if let value = myDictionary[myKey] {

}

Цей синтаксис буде виконувати лише блок if, якщо myKey знаходиться у dict, і якщо він є, то значення зберігається у змінній значення. Зауважте, що це працює для рівних фальсийних значень, таких як 0.


Дякую за це! Мені просто цікаво, але я не можу знайти цю поведінку задокументованою в довідковому об'єкті-клас class developer.apple.com/library/mac/documentation/Cocoa/Reference/… Я просто сліплю?
irh

2
Ні, ти не сліпий. Це не підкреслюється. Але видно тут (clang) і тут (сучасний objc: дуже дно) і тут (посібник зі збірок: пошуковий словник)
Andrew Hoos


9

Під час використання словників JSON:

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

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

8

Мені подобається відповідь Фернандеса, хоч ти два рази просиш об'єм.

Це також має робити (більш-менш те саме, що і А Мартіна).

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
}

Мартін і ця відповідь працюють на iPad2 iOS 5.0.1 9A405


5

Один дуже неприємний хитч, який просто витратив трохи мого часу налагодження - вам може бути запропоновано автоматичне завершення спробувати використовувати doesContainякий, здається, працює.

За винятком випадків, doesContainвикористовує порівняння id замість хеш-порівняння, яке використовує objectForKeyтак, якщо у вас є словник зі строковими клавішами, він поверне 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

Використовуючи Swift, це було б:

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

5

Так. Цей тип помилок дуже поширений і призводить до збоїв у програмі. Тому я використовую, щоб додати NSDictionary в кожен проект, як показано нижче:

//.h код файлу:

@interface NSDictionary (AppDictionary)

- (id)objectForKeyNotNull : (id)key;

@end

//.m код файлу, як показано нижче

#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

У коді ви можете використовувати наступне:

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


3

Оскільки нуль не може бути збережений у структурах даних Foundation NSNull, іноді є символом a nil. Оскільки NSNullце однотонний об'єкт, ви можете перевірити, чи NSNullє значення, яке зберігається у словнику, використовуючи порівняння прямого вказівника:

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

1
Не вийшло, коли я використовував його з NSMutableArray як об'єкт для свого ключа.
pa12

4
Чому на Землі це було прийнято? Це просто неправильно. Ця перевірка повернеться істиною, якщо NSNullекземпляр singleton був збережений у словнику як значення для ключа @"myKey". Це зовсім інша річ у тому, що ключ @"myKey"не міститься у словнику - справді ці два є взаємовиключними.
Марк Амері

Оскільки це все ще має п’ять голосів, будучи абсолютно помилковим, я можу повторити лише те, що говорить Марк: Цей код перевіряє, чи містить словник ключ з нульовим значенням, що абсолютно відрізняється від того, що він не містить ключа взагалі.
gnasher729

Це "абсолютно неправильно, але вказує на цікаву інформацію"
Fattie

Ця відповідь була відредагована, щоб уточнити, що вона робить (перевірити NSNull в дикті), а що не робити (перевірити значення в дикті)
Ендрю Хоос

2

Рішення для швидкого 4.2

Отже, якщо ви просто хочете відповісти на питання, чи містить словник ключ, запитайте:

let keyExists = dict[key] != nil

Якщо ви хочете, щоб значення і ви знаєте, що словник містить ключ, скажіть:

let val = dict[key]!

Але якщо, як це зазвичай буває, ви не знаєте, що він містить ключ - ви хочете взяти його і використовувати його, але тільки якщо він існує - тоді використовуйте щось на зразок if let:

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

1

Я б запропонував вам зберегти результат пошуку в змінній temp, перевірити, чи є змінна temp нульовою, а потім використовувати її. Таким чином, ви не шукаєте один і той же об’єкт двічі:

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

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

1

Оскільки Адіраель запропонував objectForKeyперевірити наявність ключа, але коли ви зателефонуєте objectForKeyдо словника, який не змінюється, додаток виходить з ладу, тому я виправив це з наступного способу.

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

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

}


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.