Использование NSPredicate для фильтрации NSArray на основе ключей NSDictionary

94

У меня есть множество словарей.

Я хочу отфильтровать массив по ключу.

Я пробовал это:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(SPORT ==  %@)", @"Football"];

NSArray *filteredArray = [data filteredArrayUsingPredicate:predicate];

Это не работает, я не получаю результатов. Я думаю, что я что-то не так делаю. Я знаю, что это метод, если "СПОРТ" был иваром. Думаю, наверное, другое, если это ключ.

Однако мне не удалось найти пример.

Спасибо


Обновить

Я добавил кавычки вокруг строки, которую ищу.

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(SPORT ==  '%@')", @"Football"];

Это все еще не работает.


Обновление 2

Решил это. Мне действительно пришлось удалить одинарные кавычки, что, похоже, противоречит тому, что говорит руководство.

Моя настоящая проблема в том, что у меня был вложенный массив, и я фактически не оценивал словари. Костная голова двигается.

Кори Флойд
источник
Просто упомяну, что предикат по умолчанию чувствителен к регистру (используйте [c], чтобы сделать нечувствительным).
groumpf

Ответы:

151

Он должен работать - пока переменная данных на самом деле является массивом, содержащим словарь с ключом SPORT

NSArray *data = [NSArray arrayWithObject:[NSMutableDictionary dictionaryWithObject:@"foo" forKey:@"BAR"]];    
NSArray *filtered = [data filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"(BAR == %@)", @"foo"]];

Фильтруемый в данном случае содержит словарь.

(% @ не обязательно заключать в кавычки, это делается, когда NSPredicate создает объект.)

суракен
источник
Работает ли NSPredicate на iPhone? Он отлично работает на сим-карте, но запуск на iPhone дает ошибку, что класс NSPredicate не может быть найден.
lostInTransit
NSPredicate доступен начиная с iOS 3.0.
зекель
1
чтобы прояснить для новых программистов, таких как я, ключевой строкой кода является 'NSArray * filter = [data filterArrayUsingPredicate: [NSPredicate predicateWithFormat: @ "(BAR ==% @)", @ "foo"]];' где «данные» - это массив из n словаря, который уже есть в вашем коде. (спасибо, суракен)
tmr
Что делать, если имена ключей динамические?
iPeter
27

Я знаю, что это старые новости, но добавлю два цента. По умолчанию я использую команды, LIKE[cd]а не просто [c]. [d]Сравнивает буквы с диакритическими символами. Это особенно хорошо работает в моем приложении Warcraft, где люди пишут свое имя «Vòódòó», что делает практически невозможным поиск их имени в табличном представлении. [d]Полосы их символы ударения во предикате. Таким образом, предикат @"name LIKE[CD] %@", object.namewhere object.name == @"voodoo"вернет объект, содержащий имя Vòódòó.

Из документации Apple: like [cd] означает «подобный без учета регистра и диакритических знаков»). Полное описание синтаксиса строки и список всех доступных операторов см. В разделе Синтаксис строки формата предиката .

Заряженный нейрон
источник
11
#import <Foundation/Foundation.h>
// clang -framework Foundation Siegfried.m 
    int
main() {
    NSArray *arr = @[
        @{@"1" : @"Fafner"},
        @{@"1" : @"Fasolt"}
    ];
    NSPredicate *p = [NSPredicate predicateWithFormat:
        @"SELF['1'] CONTAINS 'e'"];
    NSArray *res = [arr filteredArrayUsingPredicate:p];
    NSLog(@"Siegfried %@", res);
    return 0;
}
Исаак Осипович Дунаевский
источник
3

NSPredicate доступно только в iPhone 3.0.

Вы не заметите этого, пока не попытаетесь запустить на устройстве.

Стержень
источник
0

В Swift 3, когда вы хотите отфильтровать массив словарей с помощью предиката, основанного на словарных ключах и значениях, вы можете выбрать один из следующих шаблонов.


№1. Использование NSPredicate init(format:arguments:)инициализатора

Если вы пришли из Objective-C, init(format:arguments:) предлагает стиль кодирования "ключ-значение" для оценки вашего предиката.

Использование:

import Foundation

let array = [["key1": "value1", "key2": "value2"], ["key1": "value3"], ["key3": "value4"]]

let predicate = NSPredicate(format: "key1 == %@", "value1")
//let predicate = NSPredicate(format: "self['key1'] == %@", "value1") // also works
let filteredArray = array.filter(predicate.evaluate)

print(filteredArray) // prints: [["key2": "value2", "key1": "value1"]]

№2. Использование NSPredicate init(block:)инициализатора

В качестве альтернативы, если вы предпочитаете строго типизированные API, а не строго типизированные API, вы можете использовать init(block:)инициализатор.

Использование:

import Foundation

let array = [["key1": "value1", "key2": "value2"], ["key1": "value3"], ["key3": "value4"]]

let dictPredicate = NSPredicate(block: { (obj, _) in
    guard let dict = obj as? [String: String], let value = dict["key1"] else { return false }
    return value == "value1"
})

let filteredArray = array.filter(dictPredicate.evaluate)
print(filteredArray) // prints: [["key2": "value2", "key1": "value1"]]
Иману Пети
источник
-1

Глядя на ссылку NSPredicate , похоже, что вам нужно заключить символ подстановки в кавычки. Например, ваш текущий предикат гласит: (SPORT == Football)вы хотите, чтобы он читался (SPORT == 'Football'), поэтому ваша строка формата должна быть @"(SPORT == '%@')".

Мартин Гордон
источник
Я тоже думал, что это то, что он сказал. Видимо цитаты не нужны. Я не совсем уверен, для чего в руководстве говорится, что цитаты «следует» использовать.
Кори Флойд