Filtering an NSArray using NSPredicate's

Better and updated resource 

Hi there. This post was originally written back in late 2010. Since then the API's and methods mentioned herein have changed a lot. I wont remove this post as it still may be helpful to some but be aware that it is pretty old. 

NSHipster has an excellent, thorough and updated post. I highly recommend checking out that article.


 

Simple NSPredicate filtering

Transient

This is the most documented and suggested among the ways of filtering. A simple SQL-like query to filter down on results. NSPredicate definitions iterate through the NSArray collection running the condition statement against each row. If the row fails to match the condition it is not included in the returned result. The predicate condition can use the SQL-like statements and any Key-Value Coding syntax.

Predicate condition formats are mostly self explanatory. Take the first predicate in the example to find the records where the name contains the word 'melon'. The contains method has 2 arguments, [cd], which mean the search is case and diacritic (letters with accents, umlauts etc.) insensitive. So the matching results are Watermelon and Rockmelon.

Predicates also have more condition functions, some of which I have only touched on here:

  • beginswith : matches anything that begins with the supplied condition
  • contains : matches anything that contains the supplied condition
  • endswith : the opposite of begins with
  • like : the wildcard condition, similar to its SQL counterpart. Matches anything that fits the wildcard condition
  • matches : a regular expression matching condition. Beware: quite intense to run

The syntax also contains the following other function, predicates and operations:

  • AND (&&), OR (||), NOT (!)
  • ANY, ALL, NONE, IN
  • FALSE, TRUE, NULL, SELF

Filtering with a block

Lets look at what I mean by a block, the term may not be familiar to some. In a nut shell, a block is a function that you can pass as an argument. So if you want to iterate over something with the same simple function you don't need to put it in a separate method definition, you can define it inline with your code.

Breaking it down; A block is defined using a carat (^) and in the initial syntax has the arguments and expected return type, much like any other Objective-C method definition. The minusOne block defined above receives an integer, stored as myNumber, and then returns an int as defined. It encapsulates the code in curly braces and that is a block.

This is a really handy tool for short functions that you don't really need to define in a separate method on a class. Blocks are used mostly in iterative searches and expressions, like what we want to do in this article. But you don't use them to replace methods in a class, its just for short and 'throw-away' functions.

The NSPredicate class has the method predicateWithBlock: which accepts a block we can use to iterate over the records and return what we want. Lets look at a quick example of what this looks like:

This happens to developers at least once. We have an array of data to search through and its not in the easiest format to search through. In the example we need to single out NSDate s but the dates are stored as a NSString s, ugh! This means we have to process and format the results before we can search them. Can we do this at the same time? Yes, we can.

The example above does both the formatting to get a NSDate object and the search in the one block, impossible with a normal NSPredicate.

Running through the example, the first few lines are dedicated to setting up the test and getting a date formatter allocated. The dates are stored in an array with a reference date we want to compare them with. In this example we want to find dates after the reference date.

The block accepts 2 attributes. The first is the object ( id obj ) in the current iteration the second is a dictionary ( NSDictionary *bind ) which defined by the Apple Documentation "must contain key-value pairs for all variables in the receiver". For this purpose the second dictionary is not used. We access the obj property and do the date conversion and then run the comparison with the reference date.

Next the predicate is run through filteredArrayUsingPredicate: returning the new and filtered array.

So there we have it. Using NSPredicate s we can search and filter an array. I hope this will be as useful to you as I have found it with my own projects. Keep an eye on the development of blocks, they are a handy tool to use and definitely make development a bit easier.