What Drove Me to Finally Write a Map Method

I try to add category methods sparingly, on the principle that creating my own personal dialect of Cocoa is a bad idea. (That’s not to say I never do. Here are some.)

Much of the time I can get by without a map method by using either valueForKeyPath: or filteredArray​UsingPredicate:.

But today I ran across one that required both methods.

I have an array of notes (for Vesper’s timeline). Each note has a thumbnailID property — which is nil when there’s no thumbnail.

I wanted an array of non-nil thumbnailIDs.

I could get all the thumbnailIDs, including nils, which turn into NSNulls, with valueForKeyPath:.

NSArray *thumbnailIDs = [notes valueForKeyPath:​VSThumbnailIDKey];

And then I could get non-nil thumbnailIDs:

NSArray *thumbnailIDsMinusNulls = [thumbnailIDs filteredArray​UsingPredicate:​[NSPredicate predicateWithFormat:​@"SELF != nil"]];

And that’s not terrible. But I looked around for a better way and didn’t find anything. So I finally broke down and wrote a map method.

So now the code looks like this:

NSArray *thumbnailIDsMinusNulls = [objects qs_map:^id(id obj) {
  return ((VSTimelineNote *)obj).thumbnailID;
}];

If thumbnailID is nil, the block returns nil and it doesn’t get added to the array.

The map function (in an NSArray category) looks like this:

typedef id (^QSMapBlock)(id obj);

- (NSArray *)qs_map:(QSMapBlock)mapBlock {

  NSMutableArray *mappedArray = [NSMutableArray new];

  for (id oneObject in self) {

    id objectToAdd = mapBlock(oneObject);
    if (objectToAdd) {
      [mappedArray addObject:objectToAdd];
    }
  }

  return [mappedArray copy];
}

It’s surprising to me that there isn’t a built-in map method for NSArray. I may be the last Cocoa programmer to write my own.

12 Mar 2014

Archive