Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSPredicate match any characters

How do I construct an NSPredicate that looks for the search terms anywhere in an Arrays objects? I can't quite explain it properly, so here's an example.

NSArray *array = @[@"Test String: Apple", @"Test String: Pineapple", @"Test String: Banana"];
NSString *searchText = @"Apple";
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF CONTAINS[cd] %@", searchText];
NSArray *filteredArray = [array filteredArrayUsingPredicate:predicate];
NSLog(@"filteredArray: %@", filteredArray);

// filteredArray: (
//    "Test String: Apple",
//    "Test String: Pineapple"
// )

But if I use NSString *searchText = @"Test Str Appl"; I get zero results. I'd like it to match the same results for this string.

What I'm looking for is a search function that is similar to the "Open Quickly" menu in Xcode, where it doesn't matter if your search string is worded correctly, only that the letters are in the correct order as a match. I really hope that makes sense.

Open Quickly Menu

like image 596
squarefrog Avatar asked Feb 26 '13 14:02

squarefrog


1 Answers

The LIKE string comparison in predicates allows for wildcards * and ?, where * matches 0 or more characters. Therefore, if you transform your search text into

NSString *searchWithWildcards = @"*T*e*s*t* *S*t*r*A*p*p*l*";

by inserting a * at the beginning, between all characters, and at the end of the original search text by using something like this

NSMutableString *searchWithWildcards = [NSMutableString stringWithFormat:@"*%@*", self.searchField.text];
if (searchWithWildcards.length > 3)
    for (int i = 2; i < self.searchField.text.length * 2; i += 2)
        [searchWithWildcards insertString:@"*" atIndex:i];

then you can use

[NSPredicate predicateWithFormat:@"SELF LIKE[cd] %@", searchWithWildcards];

The predicate searches for the characters in the given order, with arbitrary other characters between them.


The transformation can for example be done with the following code:

NSMutableString *searchWithWildcards = [@"*" mutableCopy];
[searchText enumerateSubstringsInRange:NSMakeRange(0, [searchText length])
                   options:NSStringEnumerationByComposedCharacterSequences
                usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
                    [searchWithWildcards appendString:substring];
                    [searchWithWildcards appendString:@"*"];
                }];
like image 108
Martin R Avatar answered Nov 24 '22 08:11

Martin R