How To Do Amazingly Simple Searches With NSArray & NSPredicate

Post image for How To Do Amazingly Simple Searches With NSArray & NSPredicate

by mattjdrake on March 25, 2010

Cocoa-Touch ships with an super-useful class that will help you search through your lists of objects just like they were coming from a database. The class is called NSPredicate which also works with Core Data. This is very powerful stuff, especially what you are working with data intensive applications.

Here Is How NSPredicate Works

First off, keep in mind that generally we are using NSArray to manage lists of objects in our apps. The objects that NSArray stores for us are made up of properties like name, length and whatever is required for the objects in question. NSPredicate works by defining queries that select objects from an array based on the properties of the objects in the array.

Here are the steps required to search arrays with NSPredicate:

  • Find or create an NSArray full of objects with interesting properties
  • Create an NSPredicate object with the search query that you want to use
  • You can use this NSPredicate object to either filter the array or create a new array with a subset of data
  • Essentially, these three steps are all it takes to filter an array. Of course, you will need some other pieces in place to do some meaningful work. Next, I will show you a concrete example of using NSPredicate with a custom class.

    NSPredicate Search Example

    Here Is The Type Of Object We Will Query

    The first thing I did for this example was to create a custom class called RowOfData that could correspond to an individual row of data in a database.

    NOTE: this does not mean that you really use SQL per-say with NSPredicate – the types of queries are more like a regex style. Check this Apple documentation for more details on what types of queries are available to you.

    Anyway, here is the class definition for RowOfData:

    #import <Foundation/Foundation.h>
    
    @interface RowOfData : NSObject {
    	int primaryKey;
    	NSString *name;
    	NSString *job;
    	NSNumber *salary;
    }
    
    @property(nonatomic, assign) int primaryKey;
    @property(nonatomic, retain) NSString *name;
    @property(nonatomic, retain) NSString *job;
    @property(nonatomic, retain) NSNumber *salary;
    
    @end

    Each RowOfData object in the array we are about to create represents an individual and details about his or her job and salary.

    Create Your NSArray & Fill It With RowOfData Objects

    In effect, we are creating a list of objects that is a bit like a database table. Note here that listOfItems is declared as an NSMutableArray at the top of the file where this code would be located since it needs to stay in scope though-out the lifecycle of objects made from the class:

    listOfItems = [[NSMutableArray alloc] init];
    RowOfData *row;
    row = [[RowOfData alloc] init];
    row.primaryKey = 0;
    row.name = @"Tom";
    row.job = @"Car Salesman";
    row.salary = [NSNumber numberWithDouble:43900];
    [listOfItems addObject:row];
    row = [[RowOfData alloc] init];
    row.primaryKey = 1;
    row.name = @"Tina";
    row.job = @"Accountant";
    row.salary = [NSNumber numberWithDouble:145000];
    [listOfItems addObject:row];
    row = [[RowOfData alloc] init];
    row.primaryKey = 2;
    row.name = @"Harry";
    row.job = @"Programmer";
    row.salary = [NSNumber numberWithDouble:86700];
    [listOfItems addObject:row];
    row = [[RowOfData alloc] init];
    row.primaryKey = 3;
    row.name = @"Sally";
    row.job = @"Poker Player";
    row.salary = [NSNumber numberWithDouble:98000];
    [listOfItems addObject:row];
    row = [[RowOfData alloc] init];
    row.primaryKey = 4;
    row.name = @"Phil";
    row.job = @"Programmer";
    row.salary = [NSNumber numberWithDouble:2500000];
    [listOfItems addObject:row];

    To summarize, we create an array and then create each object and add each object to the array. Here is what the results would look like if we were to present this array in a table view:

    default_button.png

    Get A Subset Of Data Instead!

    So here is what you have been waiting for. To display a subset of the dataset you would need to code an NSPredicate using a query string. For instance, if we wanted to find out who in our dataset were programmers we could do something like this:

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"job == 'Programmer'"];
    [listOfItems filterUsingPredicate:predicate];

    Pretty simple huh? All you need to do is create the NSPredicate with our query and send the filterUsingPredicate message to your array using the NSPredicate object as an argument. If we tack this query to the end of our code and again run an app using a table view it would look like this:

    default_button.png

    More NSPredicate Query Examples:

    Remember that we can do this because “job” is a property of the objects contain in the array. You can query based on other properties as well. Here are some examples of what you can do:

    Find out who makes at least $100,000 per year:

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"salary >= 100000"];
    [listOfItems filterUsingPredicate:predicate];

    Find everyone who has a name that begins with “T”:

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name BEGINSWITH 'T'"];
    [listOfItems filterUsingPredicate:predicate];

    I could throw examples at you all day but ultimately this will be useful when you can apply it the objects you use in your own apps. Check this Apple documentation for a complete list of the functions you can use with NSPredicate.


    Another nice touch is that this method may be used with classes other than NSArray. One important use of NSPredicate is with Core Data. You can actually use NSPredicate to query the underlying data store managed by Core Data. This gives you a lot of powerful and efficient data management techniques right out of the box.

    Do You See A Use Case For NSPredicate In Your Next App?

    Here Is How To Fast Track Your Objective-C Programming Skills…


    Click here or the box to the right to access to your online workshop now…

    Each lesson comes packed with comprehensive video, source code and text. When appropriate I include hands-on exercises. Check out the list below to see what is specifically covered in each lesson:

    Module 1 – Getting Started With iPhone App Development

    - Lesson 1 – Overview of iPhone OS
    - Lesson 2 – Introduction to Tools: XCode, Interface Builder & iPhone Simulator
    - Lesson 3 – Your First App
    - Lesson 4 – Super-Charge XCode

    Module 2 – Learn How to Program in C

    - Lesson 1 – What is Programming?
    - Lesson 2 – C Programming Basics and Specifics
    - Lesson 3 – Functions
    - Lesson 4 – Variables and Arrays
    - Lesson 5 – Program Flow
    - Lesson 6 – Loops
    - Lesson 7 – Complex Data with Struct
    - Lesson 8 – Putting It All Together

    Module 3 – Master Object Oriented Programming With Objective-C

    - Lesson 1 – What is Object Oriented Programming?
    - Lesson 2 – Objects
    - Lesson 3 – More Strings, Lists and the For Each Loop
    - Lesson 4 – Memory Management
    - Lesson 5 – Designing Your Own Classes
    - Lesson 6 – Extending Classes With Categories
    - Lesson 7 – Protocols & Key-Value Coding

    Module 4 – No-BS Cocoa-Touch With iPhone SDK

    - Lesson 1 – Overview of Cocoa-Touch + Model-View-Controller
    - Lesson 2 – Using Interface Builder (The View)
    - Lesson 3 – Target-Action and the View in Code
    - Lesson 4 – Delegation
    - Lesson 5 – Super-Charging Your View With Interface Builder
    - Lesson 6 – Model & App Architecture

    Click here or the box to the right to access to your online workshop now

    Please share this if you like it!
    • Digg
    • del.icio.us
    • Facebook
    • Mixx
    • Google Bookmarks
    • email
    • FriendFeed
    • LinkedIn
    • MySpace
    • Ping.fm
    • StumbleUpon
    • Suggest to Techmeme via Twitter
    • Technorati
    • Tumblr
    • Yahoo! Bookmarks

    { 2 comments… read them below or add one }

    1 bsimmons March 26, 2010 at 2:16 pm

    Matt, this is a great tip/tutorial. Can you make the source code available in the post as a zip file?

    2 sdugan68 April 19, 2010 at 6:07 pm

    I defined my own class exactly how you did and everything works fine. I then tried to run a for loop after the code above. The for loop has an NSLog so I can see the output on the console. The for loop will not work after your code. If I move the for loop above your code it works fine. Here is what I have:

    for (int i; i = 2; i++) {
    NSLog(@”in loop 1 ****”);
    }

    RowOfData = *row = [[RowOfData allco] init];
    row.term = @”test 1″;
    row.definition = @”def 1″;
    [termsAndDefinitions addObject:row];
    ..
    ..
    for (int x; x = 2; x++) {
    NSLog(@”in loop 2 ****”);
    }

    [row release];

    I made the for loops simple to make sure it wasn’t something I was doing wrong. Any ideas why the for loop before the declaration works and the one after does not?

    { 1 trackback }

    Previous post:

    Next post: