iOS Objective-C how to use a predicate to search a string array for multiple non-sequential keywords?

5.1k Views Asked by At

I'm using the following code to do a search of an array, returning items that contain parts of the search query. This works well when a search query is a single word or part of a word:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"comment.text CONTAINS [cd] %@ ",_searchQuery];
NSArray *result = [_sourceArray filteredArrayUsingPredicate:predicate];

How can I expand my predicate to take a search query with multiple words and return results that contain all of the words in query?

For example:

  • Text to search "apples and oranges are fruits"
  • Query: "apple fruit"
  • The current predicate will not match this query to the text.
  • How to modify the predicate to match the text to search to that query?
2

There are 2 best solutions below

0
On BEST ANSWER

The following code enumerates the words in _searchQuery creating a predicate for each, then combines them into an NSCompoundPredicate using AND:

NSMutableArray *subpredicates = [NSMutableArray new];
[_searchQuery enumerateSubstringsInRange:NSMakeRange(0, _searchQuery.length)
                                 options:NSStringEnumerationByWords
                              usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
                           NSPredicate *subpredicate = [NSPredicate predicateWithFormat:@"comment.text CONTAINS [cd] %@ ",substring];
                           [subpredicates addObject:subpredicate];
                       }];

NSCompoundPredicate *compoundPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:subpredicates];
NSArray *result = [_sourceArray filteredArrayUsingPredicate:compoundPredicate];
1
On

If the order of your keywords is important to you, you can use a regular expression to do the job:

NSArray *const source = @[@"apples and oranges are fruits"];

static NSString *const separator = @".*";
NSArray *const keywords = @[@"apples", @"oranges"];
NSString *const pattern = [NSString stringWithFormat:@"%@%@%@", separator, [keywords componentsJoinedByString:separator], separator];
NSPredicate *const predicate = [NSPredicate predicateWithFormat:@"self MATCHES %@", pattern];

NSArray *const filtered = [source filteredArrayUsingPredicate:predicate];

Otherwise you can just OR a couple CONTAINS together, like @Larme suggested.