NSPredicate для соответствия «любой записи в базе данных NSD со значением, содержащим строку» - PullRequest
6 голосов
/ 06 апреля 2011

У меня есть массив словарей, подобный следующему:

(
        {
            Black = "?";
            Date = "????.??.??";
            Result = "*";
            SourceDate = "2007.10.24";
            White = "Mating pattern #1";
        },
        {
            Black = "?";
            Date = "????.??.??";
            Result = "*";
            SourceDate = "2008.10.24";
            White = "About this Publication";
        }
)

Я хочу предложить пользователю возможность поиска текста либо в полях «Белый» и «Черный», либов любом поле.У меня есть NSPredicate для выполнения только определенных полей:


    predicate = [NSPredicate 
                    predicateWithFormat:@"self.Black contains[cd] %@ or self.White contains[cd] %@",
                        searchText, searchText];
    [filteredGames addObjectsFromArray:[games filteredArrayUsingPredicate:predicate]];

Я не могу придумать, как сформулировать предикат, который вернет мне словари, для которых любой из объектов в пределах соответствует тексту.то есть я мог искать «2007», и он вернул бы первый словарь, но не второй.Я попробовал «self. *», Которое я не ожидал сработать, а также «ЛЮБЫЕ self.allValues», о которых я больше надеялся.На самом деле я заранее не знаю, какими будут ключи, поэтому мне нужно что-то менее конкретное.

Есть предложения?

Ответы [ 2 ]

15 голосов
/ 06 апреля 2011

Если все словари имеют одинаковый набор ключей, то вы можете сделать что-то довольно простое:

NSArray *keys = ...; //the list of keys that all of the dictionaries contain
NSMutableArray *subpredicates = [NSMutableArray array];
for (NSString *key in keys) {
  NSPredicate *subpredicate = [NSPredicate predicateWithFormat:@"%K contains[cd] %@", key, searchText];
  [subpredicates addObject:subpredicate];
}
NSPredicate *filter = [NSCompoundPredicate orPredicateWithSubpredicates:subpredicates];

Затем вы можете использовать filter для фильтрации NSArray (используя -filteredArrayUsingPredicate).

Если, с другой стороны, у вас есть массив произвольных словарей с разными ключами, вам нужно что-то более извращенное:

NSPredicate *filter = [NSPredicate predicateWithFormat:@"SUBQUERY(FUNCTION(SELF, 'allKeys'), $k, SELF[$k] contains[cd] %@).@count > 0", searchText];

Немногоо том, что это делает:

  • FUNCTION(SELF, 'allKeys') - это выполнит -allKeys на SELF (NSDictionary) и вернет NSArray всех ключей в словаре
  • SUBQUERY(allKeys, $k, SELF[$k] contains[cd] %@) - это будет повторять каждый элемент в allKeys, каждый последующий элемент помещается в переменную $k.Для каждого элемента будет выполнено SELF[$k] contains %@.Это в конечном итоге будет делать: [theDictionary objectForKey:$k] contains[cd] %@.Если это возвращает YES, то элемент $k будет агрегирован в новый массив.
  • SUBQUERY(...).@count > 0 - после нахождения всех ключей, соответствующих значениям, которые содержат ваш текст поиска, мы проверяем ипосмотрим, были ли они.Если они есть (т. Е. Размер массива больше 0), тогда весь словарь будет частью окончательного отфильтрованного массива.

Я рекомендую использовать первый подход, если привсе возможно.SUBQUERY и FUNCTION немного загадочны, и первое из них намного легче для понимания.


И вот еще один способ, который вы фактически почти имели в своем вопросе.Вместо ANY SELF.allValues contains[cd] %@ вы можете ANY FUNCTION(SELF, 'allValues') contains[cd] %@.Это эквивалентно моему безумию, но намного проще.Спасибо вам за то, что вы думаете об использовании ANY (я обычно забываю, что он существует).

EDIT

Причина, по которой SELF.allValues не работает, заключается в том, чтоэто интерпретируется как путь к ключу, и -[NSDictionary valueForKey:] должен быть таким же, как -[NSDictionary objectForKey:].Подвох в том, что если вы добавите к ключу @, то он перейдет к [super valueForKey:], что будет делать то, что вы ожидаете.Таким образом, вы действительно могли бы сделать:

ANY SELF.@allValues contains[cd] %@

Или просто:

ANY @allValues contains[cd] %@

И это будет работать (и это лучший и самый простой подход).

0 голосов
/ 06 апреля 2011

Если вы хотите сопоставить объект в словаре, вы можете сделать это с помощью цикла for, но это может занять много времени -

for(int i=0; i< [array count]; i++)
{

    NSDictionary *dic = [array objectAtIndex:i];

    if([[dic objectForKey:@"Black"] isEqualToString:@"2007"])
    {
    //here is the match
    }
}
...