Як перевірити, чи містить ключ 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.