Map in Objective-C

Learning new languages is great because they can force you to think in other ways. I was recently introduced to map, a functional programming technique that applies a given function to each element of a list, returning a list of results. ¬†I’ll let you check out the formal Wikipedia explanation of it. To clarify, map shouldn’t be confused with NSArray – (void)makeObjectsPerformSelector: as they two very different things. Lets take a look at an example of map:

NSArray *numbers = @[ @1, @2, @3, @4, @5];
[multipliedNumbers addObjectsFromArray [numbers map:^id(id object) {
    return object * 2;
}]];
 
// multipliedNumbers now contains [2, 4, 6, 8, 10];

Okay thanks for the academic explanation, what about a more real world example?

[fullNameArray addObjectsFromArray:[peopleArray map:^id(id object) {
    Person *person = (Person *)object;
    return [NSString stringWithFormat:@"%@ %@", person.firstName, person.lastName];
}]];

Another example using core data, combining two results.

NSArray *fetchedFriends = [myManagedObjectContext executeFetchRequest:friendsFetchRequest error:nil];
NSArray *fetchedEnemies = [myManagedObjectContext executeFetchRequest:enemyFetchRequest error:nil];
 
[frenemies addObjectsFromArray:[fetchedFriends map:^id(id object) {
    FriendEntity *friendEntity = (FriendEntity *)object;
    Frenemy *myFrenemy = [[Frenemy alloc] init];
    myFrenemy.fullName = friendEntity.firstName;
    myFrenemy.lastName = friendEntity.lastName;
    return myFrenemy;
}]];
 
[frenemies addObjectsFromArray:[fetchedEnemies map:^id(id object) {
    EnemyEntity *enemyEntity = (EnemyEntity *)object;
    Frenemy *myFrenemy = [[Frenemy alloc] init];
    myFrenemy.fullName = enemyEntity.firstName;
    myFrenemy.lastName = enemyEntity.lastName;
    return myFrenemy;
}]];

NSArray currently doesn’t have map, so we’ll have to implement it using a category.

#import "Foundation/Foundation.h"
 
typedef id(^MapBlock)(id object);
 
@interface NSArray (FP)
 
- (NSArray *)map:(MapBlock)block;
 
@end

As you can see we first have to define the actual block as a typedef. A block in Objective-C is essentially just a C function. We give it the name “MapBlock”.

#import "NSArray+FP.h"
 
@implementation NSArray (FP)
 
- (NSArray *)map:(MapBlock)block {
    NSMutableArray *resultArray = [[NSMutableArray alloc] init];
    for( id obj in self ) {
        [resultArray addObject:block(obj)];
    }
    return resultArray;
}

You can download the source on Github.