Saturday, March 12, 2011

NSPredicate: simple Cocoa examples

I'm exploring predicates and expressions in Cocoa using Objective-C. Actually, I'd like to use PyObjC, but this is tricky enough that I think it's better to start with Objective-C.

Ultimately I'd like to write complex predicates, for example, to filter using either a custom function or a block. The reason I need this is to validate edits to an NSTableView using bindings. According to the docs:


A predicate is a logical operator that returns a Boolean value (true or false). There are two types of predicate; comparison predicates, and compound predicates:

● A comparison predicate compares two expressions using an operator. The expressions are referred to as the left hand side and the right hand side of the predicate (with the operator in the middle). A comparison predicate returns the result of invoking the operator with the results of evaluating the expressions.

● A compound predicate compares the results of evaluating two or more other other predicates, or negates another predicate.


All the code is like this:


#import <Foundation/Foundation.h>

int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
.. specific code here ..
[pool drain];
return 0;
}


compiled and run like this:


> gcc -o test pred.m -framework Foundation
> ./test

Example 1. Predicate with a format string using "like"


(the c is a case-insensitive compare, the * is a wild card):

NSArray *A = [NSArray arrayWithObjects:
@"John",@"Paul",@"George",@"Ringo",nil];
NSPredicate *p = [NSPredicate predicateWithFormat:
@"SELF like [c] 'g*' "];    
NSLog(@"%@", [[A filteredArrayUsingPredicate:p] objectAtIndex:0]);

> ./test
2011-03-12 09:38:55.379 test[67230:903] George

Example 2. Format string with a mathematical operator



NSArray *A = [NSArray arrayWithObjects:
[NSNumber numberWithInt:3],
[NSNumber numberWithInt:5],nil];
NSPredicate *p = [NSPredicate predicateWithFormat:
@"SELF > 4 "];    
NSLog(@"%@", [[A filteredArrayUsingPredicate:p] objectAtIndex:0]);

> ./test
2011-03-12 09:38:22.313 test[67217:903] 5


Note: also works with the usual kind of string formatting :@"SELF > %d ", 4

Example 3: Format string using a key path



NSArray *A = [NSArray arrayWithObjects:
[NSMutableDictionary dictionaryWithObject:@"x" forKey:@"name"],
[NSMutableDictionary dictionaryWithObject:@"y" forKey:@"name"], nil];

NSPredicate *p = [NSPredicate predicateWithFormat:@"name like %@", @"x"];    
NSLog(@"%@", [[A filteredArrayUsingPredicate:p] objectAtIndex:0]);

> ./test
2011-03-12 10:01:43.963 test[67885:903] {
name = x;
}

Example 4: Format string substituting a dynamic key path



NSArray *A = [NSArray arrayWithObjects:
[NSMutableDictionary dictionaryWithObject:@"x" forKey:@"name"],
[NSMutableDictionary dictionaryWithObject:@"y" forKey:@"name"], nil];
//NSLog(@"%@", [[A objectAtIndex:0] description]);

NSString *name = @"name";
NSString *value = @"x";
NSPredicate *p = [NSPredicate predicateWithFormat:@"%K like %@", name, value];    
NSLog(@"%@", [[A filteredArrayUsingPredicate:p] objectAtIndex:0]);

> ./test
2011-03-12 09:29:59.220 test[67013:903] {
name = x;
}

Example 5. Compound predicate



NSArray *theKeys = [NSArray arrayWithObjects:@"name",@"value",nil];
NSArray *o1 = [NSArray arrayWithObjects:@"x",[NSNumber numberWithInt:5],nil];
NSArray *o2 = [NSArray arrayWithObjects:@"y",[NSNumber numberWithInt:7],nil];
NSArray *o3 = [NSArray arrayWithObjects:@"z",[NSNumber numberWithInt:9],nil];

NSArray *A = [NSArray arrayWithObjects:
[NSMutableDictionary dictionaryWithObjects:o1 forKeys:theKeys],
[NSMutableDictionary dictionaryWithObjects:o2 forKeys:theKeys], nil];
NSLog(@"%d", [A count]);

NSPredicate *p = [NSPredicate predicateWithFormat:
@"(value < %@) OR (name == %@)", [NSNumber numberWithInt:6], @"y"]; 
NSArray *fA = [A filteredArrayUsingPredicate:p];
NSLog(@"%d", [fA count]);
for (id obj in fA) {
NSLog(@"%@", [obj description]);
}

> ./test
2011-03-12 10:34:11.921 test[68778:903] 2
2011-03-12 10:34:11.924 test[68778:903] 2
2011-03-12 10:34:11.925 test[68778:903] {
name = x;
value = 5;
}
2011-03-12 10:34:11.925 test[68778:903] {
name = y;
value = 7;
}