Michael Crosby


Objective - C LINQ

I have been working on two iOS applications over the past few days and have been longing for a LINQ like syntax in Objective-C. LINQ is an amazing way to work with collections and it really adds a productivity boost to your coding. Since Objective C does not have LINQ I thought I would code it myself. Right now it only supports non-destructive operations but here it is.

@interface NSArray (IQuery)

- (id) first;
- (id) last;
- (id) firstOrDefault: (int(^)(id)) predicate;
- (id) firstOrDefault;

- (BOOL) any;
- (BOOL) any:(int (^)(id)) predicate;
- (BOOL) all:(int (^)(id)) predicate;
- (NSArray *) ofClass: (Class) class;
- (NSArray *) where: (int (^)(id)) predicate;
- (NSArray *) select: (id (^)(id)) predicate;
- (NSArray *) skip: (id (^)(id)) predicate;
- (void) forEach: (void(^)(id)) action;

@end

This is the current interface. Most methods return BOOL or an NSArray so that the query operations can be chained together.

Code

What we want to do in this example is to take an array with strings and ints, filter it by class, then by strings starting with 'h'. Here is how you do it:

NSArray * array = [[NSArray alloc] initWithObjects:
                          @"hot dog", 
                          @"hamburger", 
                          @"pizza", 
                          @"taco", 
                          @"pop", 
                          0, 
                          5, 
                          3, 
                          nil];

NSArray * filter = [[array 
                           ofClass:[NSString class]]
                           where:^(NSString * item) {
                            return [item characterAtIndex:0] == 'm';
                        }];

[filter forEach:^(NSString * i) { NSLog(i); }];

So that is it. The first IQuery method is "ofClass". This takes a class and returns an NSArray of all the items in the array that are of that type(class). It is the same idea as C#'s OfType.

Next is "where". This takes a predicate block that will return any item that satisfies the condition. The syntax is not as clean as C# implementation but it's a start.

Now after we have this filtered list with the chain of calls to "ofClass" and "where", we can now use the "forEach" method to enumerate the array, passing a block as the action to perform on each item.

Select

One last thing, if you want to select a certain property from an item in the array, you can use the "select" method to do so. Here we select the price for each of the items.

NSArray * array = [[NSArray alloc] initWithObjects:
                         [[Items alloc] initWithNameAndPrice:@"hamburger" :[NSNumber numberWithInt:3]], 
                         [[Items alloc] initWithNameAndPrice:@"hot dog" :[NSNumber numberWithInt:5]],
                         [[Items alloc] initWithNameAndPrice:@"pop" :[NSNumber numberWithInt:2]], 
                       nil];

NSArray * prices = [array select:(id)^(Items * item) {
    return item.price;
}];

This will return an array of NSNumber items. Nothing more.

That's all for now. If you are interested in the project I will have the binary to download soon. Just email me with any comments or questions.

comments powered by Disqus