Как проверить, содержит ли ключ NSDictionary или NSMutableDictionary?


456

Мне нужно проверить, есть ли у dict ключ или нет. Как?


4
(Только для тех, кто ищет здесь. Обратите внимание, что это очень старый вопрос . Ответ SaintMacintosh, представленный ниже, - это состояние дел, оно на пять лет впереди этого QA. Надеюсь, это поможет.)
Толстяк

1
На самом деле, ответ Энди Дента также дает современное состояние и больше контекста. И он сделал это раньше, чем SaintMacintosh. Сделайте себе одолжение, прокрутите вниз немного больше.
Питер Кэмпф

1
Он использует: keysByName [test]! = Nil проверка! = Nil является избыточной и ИМХО менее читаемой. Я просто хотел поделиться версией TL; DR для людей, которые ищут синтаксис.
Эндрю Хоос

Я согласен с @SaintMacintosh. его ответ гораздо более лаконичен.
Стив Шварц

Если вы хотите , чтобы проверить, NSDictionaryсодержит какой - либо ключ (неспецифический) следуешь использовать [dictionary allKeys].count == 0Если countэто 0нет ключей в NSDictionary.
Александр Азизи

Ответы:


768

objectForKey вернет ноль, если ключ не существует.


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

232
Это невозможно. Вы не можете добавить ноль в NSDictionary. Вы должны использовать [NSNull null]вместо этого.
Оле Бегеманн

10
@fyodor nsdictionay будет выдавать исключение NSInvalidArgumentException, если вы попытаетесь вставить значение nil, поэтому никогда не должно быть случая, когда существует ключ, но соответствующее значение равно nil.
Брэд App Guy

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

6
Вы абсолютно НИКОГДА не хотите использовать valueForKey для словаря JSON. Это потому, что JSON может содержать любую строку в качестве ключа. Например, если он содержит «@count» в качестве ключа, то objectForKey: @ «@ count» даст правильное значение, а valueForKey: @ «@ 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, и вы не сможете понять, почему ваша программа работает медленно. Почти все быстро один раз или в небольших случаях. Но поскольку программы растут, используя неправильную структуру данных (например, массив вместо dict в первом примере), это будет стоить вам очень дорого.
Эндрю Хос

2
Ожидается проверка словаря (или набора) на наличие ключа O (1) с наихудшим случаем O (n). Не O (log (n)). Документация Apple ясно объясняет это.
Эндрю Хоос,

@JoeBlow « оживляет трехмерные точки Mecanim» Похоже, вы путаете Cocoa Touch / Objective-C с Unity Engine / C #. Это правда, что Unity Engine ужасно неэффективен на платформах, на которых он работает. Тем не менее, это вовсе не так, что приложения Objective-C / Swift по своей сути неэффективны, и что большинство опытных разработчиков iOS не осознают эффективность своего кода. Что касается «только для (4 живых) программистов производительности» - вы явно не разработчик игр. Отличная графика и геймплей на скорости 60FPS без потери заряда аккумулятора - это постоянная проблема.
Слипп Д. Томпсон

98

Более поздние версии Objective-C и Clang имеют современный синтаксис для этого:

if (myDictionary[myKey]) {

}

Вам не нужно проверять равенство с nil, потому что в словарях (или массивах) могут храниться только объекты Objective C, не являющиеся nil. И все объекты Objective-C являются истинными значениями. Даже @NO, @0и [NSNull null]оценить как истинное.

Изменить: Swift теперь вещь.

Для Swift вы бы попробовали что-то вроде следующего

if let value = myDictionary[myKey] {

}

Этот синтаксис будет выполнять блок if, только если myKey находится в dict, и если это так, то значение сохраняется в переменной value. Обратите внимание, что это работает даже для значений фальси, таких как 0.


Спасибо за это! Мне просто любопытно, но я не могу найти это поведение, задокументированное в справочнике классаjective-c developer.apple.com/library/mac/documentation/Cocoa/Reference/… Я просто слепой?
IRH

2
Нет, ты не слепой. Это не подчеркивается. Но видимый здесь (лязг) и здесь (современный объект: очень низ) и здесь (руководство по коллекциям: поиск по словарю)
Эндрю Хоос


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вместо сравнения хешей используется сравнение идентификаторов, 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

// код файла ниже

#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

Потому что nil не может быть сохранен в структурах данных Foundation NSNull, иногда представляет собой nil. Поскольку NSNullэто одноэлементный объект, вы можете проверить, NSNullявляется ли значение, сохраненное в словаре, с помощью прямого сравнения указателей:

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

1
Не сработало, когда я использовал его с NSMutableArray в качестве объекта для моего ключа.
pa12

4
С какой стати это проголосовало? Это просто неправильно. Эта проверка вернет true, если NSNullэкземпляр singleton был сохранен в словаре в качестве значения для ключа @"myKey". Это совершенно другая вещь, если ключ @"myKey"отсутствует в словаре - на самом деле оба они взаимоисключающие.
Марк Эмери

Поскольку у этого все еще есть пять голосов, будучи полностью неправильным, я могу только повторить то, что говорит Марк: Этот код проверяет, содержит ли словарь ключ с нулевым значением, которое полностью отличается от того, что ключ вообще не содержит.
gnasher729

Это «совершенно неправильно, но указывает на интересную информацию»
Толстяк

Этот ответ был отредактирован, чтобы уточнить, что он делает (проверяет NSNull в dict) и что он не делает (проверяет значение в dict)
Эндрю Хус,

2

Решение для swift 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

Я бы посоветовал вам сохранить результат поиска во временной переменной, проверить, является ли временная переменная ноль, а затем использовать его. Таким образом, вы не увидите один и тот же объект дважды:

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

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

1

Как Adirael предложил 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.