NSPredicate on an NSArray to search for any object

ICL1901 picture ICL1901 · Sep 16, 2013 · Viewed 23.8k times · Source

I have an array of objects with names, addresses, tel. nos, etc.

I want to be able to search the array for any occurrence of a term - whether in the name field, the address field, etc.

I have something like this in mind :

-(void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope {
    // Update the filtered array based on the search text and scope.
    // Remove all objects from the filtered search array


    [self.searchResults removeAllObjects];
    // Filter the array using NSPredicate
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF contains[c] %@",searchText];
    searchResults = [NSMutableArray arrayWithArray:[contactArray filteredArrayUsingPredicate:predicate]];
}

This causes an exception "Can't use in/contains operator with collection".

UPDATE. I can now search on up to three fields. When I add a fourth (in any sequence), I get this exception: "Unable to parse the format string ..."

The Predicate code is now:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.narrative contains[c] %@ OR SELF.category contains[c] %@ OR SELF.date contains[c] OR SELF.name contains[c] %@", searchText, searchText, searchText, searchText];

     searchResults = [NSMutableArray arrayWithArray:[allDreams filteredArrayUsingPredicate:predicate]];

Is three a limit of predicate search fields? How do I get around this? Thanks again.

Answer

Monolo picture Monolo · Sep 16, 2013

Just use a predicate string that checks for them all:

@"name contains[cd] %@ OR address contains[cd] %@"

you can add as many as you want.

The only downside is that you'll need to add the same search string for each field you want to test, which can seem a bit ugly.

If your objects are dictionaries, then there is a way to truly search all values without necessarily knowing their names at compile time, using a subquery.

It works like this:

@"subquery(self.@allValues, $av, $av contains %@).@count > 0"

It uses the @allValues special key (or method call if you prefer) for dictionary objects and uses that to filter any value that contains your search string. If any is found (i.e., the count is positive), the object is included in the results.

Notice that this will examine all values indiscriminately, even those that you don't want to include if you have any in your dictionary.