inessential by Brent Simmons

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) {</br>   return ((VSTimelineNote \*)obj).thumbnailID;</br> }];

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 {</br> </br>   NSMutableArray \*mappedArray = [NSMutableArray new];</br> </br>   for (id oneObject in self) {</br> </br>     id objectToAdd = mapBlock(oneObject);</br>     if (objectToAdd) {</br>       [mappedArray addObject:objectToAdd];</br>     }</br>   }</br> </br>   return [mappedArray copy];</br> }

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.