Jonathan Gurebo
Jonathan Gurebo

Reputation: 1119

Objective-C - Better search than NSCaseInsensitiveSearch? Keywords? NSPredicate?

currently i am using this to search through my array of dictionaries (from plist file):

for(NSDictionary *wine in mainArray)
    {
        NSString *wineName = [wine objectForKey:@"name"];
        NSRange range = [wineName rangeOfString:searchText options:NSCaseInsensitiveSearch];
        if(range.location != NSNotFound)
            [searchArray addObject:wine];
    }

In this question I am going to have this plist as an example:

<array>
<dict>
  <key>name</key>
  <string>Banana One (Yellow)</string>
  <key>value</key>
  <string>1</string>
</dict>
<dict>
  <key>name</key>
  <string>Apple Two (White)</string>
  <key>value</key>
  <string>2</string>
</dict>
<dict>
  <key>name</key>
  <string>Pineapple Three (Orange)</string>
  <key>value</key>
  <string>3</string>
</dict>
<dict>
</array>

(Do not care about the value)

Ok, When I'm using the UISearchBar with NSCaseInsensitiveSearch i can simply search through the array (ObjectForKey=@"name") without think about Case. Good!

for example i can search for:@"banana" and find the :Banana One (Yellow)

and i can search for:@"two" and find the :Apple Two (White)

and i can search for:@"banana one" and find the :Banana One (Yellow)

I can also search for:@"Orange" and find the :Pineapple Three (Orange)

But only if the search is in right sequence

I can't search for:@"One Banana" or @"Yellow One" or @"Yellow Banana" I would really like to be able to search like this, maybe using keywords?

maybe something like this?:

for(NSDictionary *wine in mainArray)
{
NSArray *myArray = [searchText componentsSeparatedByString:@" "];
    NSString *wineName = [wine objectForKey:@"name"];
    NSRange range = [wineName rangeOfString:myArray options:NSCaseInsensitiveSearch];
    if(range.location != NSNotFound)
        [searchArray addObject:wine];
}

I know that this really won't work but I think you know what I mean? So, How to be able to search for example:@"one banana" and find :Banana One (Yellow)

Upvotes: 1

Views: 1799

Answers (3)

Jonathan Gurebo
Jonathan Gurebo

Reputation: 1119

SOLVED

Thanks DevFan for your answer! I needed to edit some things for example SELF to SELF.name to specify the key in my array of dictionaries.

NSArray *searchComoponentsWithEmptyStrings = [searchText componentsSeparatedByString:@" "];
        NSMutableArray *searchComoponents = [searchComoponentsWithEmptyStrings mutableCopy];
        [searchComoponents removeObject:@""]; //remove empty strings from the array (only works with NSMutableArray)

        NSMutableString *format = [NSMutableString stringWithFormat:@"SELF.name CONTAINS[c] '%@'",[NSString stringWithFormat:@"%@",searchComoponents[0]]];
        for (int i = 1; i < searchComoponents.count; i++) {
            [format appendFormat:@"AND SELF.name CONTAINS[c] '%@'",[NSString stringWithFormat:@"%@",searchComoponents[i]]];
        }
        NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"%@",format]];
        searchArray = [mainArray filteredArrayUsingPredicate:predicate];

and now I can also search for @"banana " (with space at the end) and still find Banana One (Yellow)

Upvotes: 1

jraufeisen
jraufeisen

Reputation: 3897

I worked out following solution using NSPredicate.

    NSArray *objects = @[@"Banana One",@"Apple Two"];
    NSString *searchString = @"one banana";
    NSArray *searchComoponents = [searchString componentsSeparatedByString:@" "];

    NSMutableString *format = [NSMutableString stringWithFormat:@"SELF CONTAINS[c] \"%@\"",[NSString stringWithFormat:@"%@",searchComoponents[0]]];
    for (int i = 1; i < searchComoponents.count; i++) {
        [format appendFormat:@" AND SELF CONTAINS[c] \"%@\"",[NSString stringWithFormat:@"%@",searchComoponents[i]]];
    }
    NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"%@",format]];
    NSArray *filtered = [objects filteredArrayUsingPredicate:predicate];

For a short explanation: SELF is the current object in the array, which should be proofed, CONTAINS looks, whether the left object contains the right object, and [c] makes it all caseinsensitive. So for every keyword in the searchString, you add the condition to the predicateformat, so that at the end, you should have a NSPredicate with this format: SELF CONTAINS[c] object1 AND SELF CONTAINS[c] object2 .... The last line creates a filtered array using this NSPredicate. For more information on the predicateFormat look here.

Upvotes: 1

timmeke
timmeke

Reputation: 36

Maybe you should split the NSString "Banana One (Yellow)" in an array ("Banana", "One", "(Yellow)") and check if one of those contains to your searchtext.

    NSString *searchText = @"One";
    NSString *txt = @"Banana One (Yellow)";
    NSArray* splitString = [txt componentsSeparatedByString:@" "];
    for(int i = 0; i < [splitString count]; i++){
        if([[splitString objectAtIndex:i] rangeOfString:searchText].location != NSNotFound) {
            return txt;
        }
    }

Upvotes: 0

Related Questions