Tuesday, 2 September 2014

Filtering a NSArray using NSPredicate

In this post, i'll walk you through how to filter an NSArray using NSPredicates. 
The first thing you'll need to decide is if you're filtering a number or a string.
For strings, you can use any of the following filters: (NOTE: must be all caps)
  • BEGINSWITH
  • ENDSWITH
  • LIKE
  • MATCHES
  • CONTAINS
For numbers, you can use:
  • !=
  • >
  • >=
  • <
  • <=
  • ==
Now that you know what kind of data you're going to filter on, we can build our predicate.
Lets first assume we're filtering an array of strings:

NSArray* data = @[@"Grapes", @"Apples", @"Oranges];
Using this array, lets filter on the letter "a"
This means we'll want to use CONTAINS.
So if we want to build a hard-coded string it would look something like:
NSString* filter = @"%K CONTAINS %@";
We'll then take our filter string and create a predicate with it: 
NSPredicate* predicate = [NSPredicate predicateWithFormat:filter, @"SELF", @"a"];
Now we can get our filtered out data:
NSArray* filteredData = [data filteredArrayUsingPredicate:predicate];
The result will be an array that contains 2 values:
"Grapes" and "Oranges"
Hmm.... why wasn't "Apples" included, it clearly contains the letter "a". 
Well, b/c the "a" is a capital "A" it wasn't included. 
In order for it to be included, we need to tell the filter to be case insensitive. We do that by passing the parameter "c"
NSString* filter = @"%K CONTAINS[c] %@";
Now, if we rerun our filter with this new string, there will be 3 values returned.
Its worth noting that there is one additional parameter you can also pass in: "d". It stands for diacritic insensitive. So if you wanted your filter to be both case and diacritic insensitive just use:
NSString* filter = @"%K CONTAINS[cd] %@";
i'm sure, you may be wondering what the %K is in the filter string is. That represents the property for each item in the array that we're filtering on. Because our array is just an array of strings, we're not filtering on a particular property. So in this case, we're passing in "SELF".  
Lets pretend we have a more complex object now. We'll call it Person and it has a property called "firstName".
If we wanted to filter on our array of Person objects, we would have to just modify the values we pass in to our predicate:
NSPredicate* predicate = [NSPredicate predicateWithFormat:filter, @"firstName", @"a"];
When we pass this predicate into filteredArrayUsingPredicate: our Person array will be filtered with Person objects whose firstName contains the letter "a".
To take this one step further, lets add a lastName property to our Person object. 
How do we filter both the firstName and lastName properties at the same time?
For this scenario, the requirement is that we want to filter our Person array by any first or last name that contains the letter a. 
To do this, we'll need to use a different method to create our NSPredicate: predicateWithFormat:argumentArray:
We'll also need a new filter string:
NSString* filter = @"%K CONTAINS[cd] %@ || %K CONTAINS[cd] %@";
Then we need to build an array of arguments. The array is just made up of all of the arguments that make up the missing fields in the filter string: 
NSArray* args = @[@"firstName", @"a", @"lastName", @"a"];
Finally we can build our predicate:
NSPredicate* predicate = [NSPredicate predicateWithFormat:filter argumentArray:args];
We can then take that predicate and filter our array the way we did above.

No comments:

Post a Comment