NSPredicate, игнорирующий запятые?

Я реализовал UISearchDisplayController, который позволяет пользователям выполнять поиск в таблице. В настоящее время предикат, который я использую для поиска, выглядит следующим образом:

NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:@"Name contains[cd] %@", searchText];

Теперь предположим, что пользователь ищет «бобы, приготовленные», соответствующие совпадения найдены в таблице. Но если пользователь введет текст поиска как «приготовленная фасоль» без запятой, совпадений найдено не будет.

Как я могу переписать свой предикат, чтобы «игнорировать» запятые при поиске? Другими словами, как я могу переписать его так, чтобы он считал «приготовленные бобы» равными «приготовленным бобам» (БЕЗ ЗАПЯТОЙ)?


person gossainapps    schedule 15.06.2012    source источник
comment
Если все, что вы хотите сделать, это удалить запятые, тогда подойдет searchText = [searchText stringByTrimmingCharactersInSet:characterSet] или searchText = [searchText stringByReplacingOccurrencesOfString:@"," withString:@""].   -  person cdelacroix    schedule 16.06.2012
comment
Спасибо, но моя проблема на самом деле в том, что запятая на самом деле не только в поисковом запросе, но и в ключе (в случае @Name), который ищется. Так есть ли способ поиска, игнорируя знаки препинания, точно так же, как мы можем искать, игнорируя регистр?   -  person gossainapps    schedule 16.06.2012
comment
Хорошо, вам нужна полнофункциональная поисковая система, у вас не просто проблемы с запятыми. Возможно, вы также хотели бы, чтобы поиск «Приготовленные бобы» возвращал тот же результат, а также «Приготовленные бобы» и, возможно, «вареные бобы» и «почему бы и не запеченные бобы». Способ — токенизировать запись, отфильтровать пустые слова и построить сложный запрос. Конечно, эти действия зависят от языка. Несколько лет назад я делал все вручную, теперь мне любопытно, упомянет ли кто-нибудь хорошую стороннюю библиотеку для выполнения этой работы.   -  person cdelacroix    schedule 16.06.2012
comment
Да, верно, я бы хотел, чтобы поиск был более удобным. Я смог добиться этого, обновив свою базу данных новым ключом, который содержит то же имя, но со всеми удаленными знаками препинания, запятыми и т. д. Так что теперь я могу одновременно искать этот нормализованный ключ. Это не идеально, но это намного лучше и было довольно легко сделать. Большое спасибо за ответ!   -  person gossainapps    schedule 16.06.2012


Ответы (3)


Сначала оговорка:

Я думаю, что вы пытаетесь добавить некоторую «нечеткость» в свой алгоритм поиска, поскольку вы хотите, чтобы ваше совпадение было нечувствительным к определенным различиям в пользовательском вводе.

Предикаты (которые являются логическими конструкциями) по своей природе не являются нечеткими, поэтому существует скрытое несоответствие импеданса между проблемой и выбранным инструментом.

В любом случае, одним из способов сделать это может быть добавление метода в класс объекта вашей модели.

В этом методе вы можете очистить строку имени, чтобы она содержала только самые основные символы, скажем, цифры, буквы ascii и пробел.

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

Вот реализация, которая удаляет знаки препинания, акценты и диакритические знаки:

- (NSString *)simplifiedName
{
    // First convert the name string to a pure ASCII string
    NSData *asciiData = [self.name dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];

    NSString *asciiString = [[[NSString alloc] initWithData:asciiData encoding:NSASCIIStringEncoding] lowercaseString];

    // Define the characters that we will allow in our simplified name
    NSString *searchCharacters = @"0123456789 abcdefghijklmnopqrstuvwxyz";

    // Remove anything else
    NSString *regExPattern = [NSString stringWithFormat:@"[^%@]", searchCharacters];

    NSString *simplifiedName = [asciiString stringByReplacingOccurrencesOfString:regExPattern withString:@"" options:NSRegularExpressionSearch range:NSMakeRange(0, asciiString.length)];

    return simplifiedName;
}

Теперь можно сделать предикат для поиска по упрощенному имени:

NSPredicate *pred = [NSPredicate predicateWithFormat:@"self.simplifiedName = %@", searchString];

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

Наконец, метод simplifiedName также можно добавить, внедрив категорию в класс объекта модели, поэтому вам не нужно изменять его код, что удобно, если ваш класс объекта определен в файле, автоматически сгенерированном Core Data.

person Monolo    schedule 17.06.2012
comment
кажется, self.simplifiedName не подходит для CoreData. Есть мысли, как проделать этот трюк с Core Data? - person beryllium; 24.09.2014

Это может быть немного хакерским, но вы можете просто удалить запятую из поискового запроса.

Пример:

searchText = [searchText stringByReplacingOccurrencesOfString:@"," withString:@""];
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:@"Name contains[cd] %@", searchText];
person Chance Hudson    schedule 15.06.2012
comment
Привет, спасибо за ответ, но моя проблема в том, что запятая на самом деле не только в поисковом запросе, но и в ключе (в случае @Name), который ищется. - person gossainapps; 16.06.2012

Лучшее решение, которое я нашел для этого типа проблемы, — это фактически добавить запись в каждый словарь элементов с тем же именем, но с удалением всех знаков препинания, запятых, тире и т. д., как в этот ответ

person gossainapps    schedule 16.06.2012