Как проверить, содержит ли ключ 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.