<?xml version="1.0" encoding="UTF-8" standalone="yes"?><oembed><version><![CDATA[1.0]]></version><provider_name><![CDATA[Important Shock]]></provider_name><provider_url><![CDATA[https://importantshock.wordpress.com]]></provider_url><author_name><![CDATA[Patrick Thomson]]></author_name><author_url><![CDATA[https://importantshock.wordpress.com/author/importantshock/]]></author_url><title><![CDATA[Map, Filter and Reduce in&nbsp;Cocoa]]></title><type><![CDATA[link]]></type><html><![CDATA[<p>After working in Scheme, Python or Ruby, all of which (more or less) support function objects and the <code>map()</code>, <code>filter()</code> and <code>reduce()</code> functions, languages that don&#8217;t seem to be somewhat cumbersome. Cocoa manages to get these paradigms <em>almost</em> correctly implemented.</p>
<p><strong>map()</strong></p>
<p>One would think that Objective-C&#8217;s ability to pass functions around as objects in the form of selectors would make writing a map() method easy. Observe, however, the crucial differences of the NSArray equivalent to <code>map()</code>. (For those unfamilar with it, <code>map()</code>, when given an array and a method/function taking one argument, returns the result of <em>map</em>ping the function onto each item of the array.)</p>
<p>From the NSArray documentation:</p>
<blockquote><p> makeObjectsPerformSelector:</p>
<p>Sends the aSelector message to each object in the array, starting with the first object and continuing through the array to the last object.</p>
<p>&#8211; (void)makeObjectsPerformSelector:(SEL)aSelector<br />
Discussion</p>
<p>The aSelector method must not take any arguments. It shouldn’t have the side effect of modifying the receiving array.</p></blockquote>
<p>This is different on no fewer than two levels. Firstly, <strong>even in the NSMutableArray subclass</strong>, this method is not allowed to have side effects. Frankly, I can think of few situations in which I would need to map an idempotent function onto an array; the point of <code>map()</code> is to be able to apply a function quickly to every element of an array <strong>and get back the changes!</strong> Secondly, an unaware or hurried programmer would think that this function was implemented so that one could write code like this:</p>
<p><code>- (void)printAnObject:(id)obj<br />
{<br />
NSLog([obj description]);<br />
}</code></p>
<p>and then do this:<br />
<code>[anArray makeObjectsPerformSelector: @selector(printAnObject:)];</code></p>
<p>This is not the case &#8211; the above code would just <em>make each element call printAnObject:, not call printAnObject with each element</em>. I&#8217;m sure that to some it seems obvious, but I, for one, found this to be an insidiously tricky wart.</p>
<p><strong>However, there is a (limited) workaround.</strong></p>
<p>NSArray&#8217;s <code>valueForKey:</code> method (somewhat counter-intuitively) returns the result of invoking valueForKey on each of the array&#8217;s elements. As such, one can map KVC functions onto arrays. For example:</p>
<p><code>NSArray *arr = [NSArray arrayWithObjects: @"Get", @"CenterStage", @"0.6.2", @"because", @"it", @"rocks", nil];</code><br />
<code>[arr objectForKey: @"length"]; // returns [3, 10, 6, 7, 2, 5]</code></p>
<p>This can be used in many helpful ways; sadly, it only works on KVC-compliant properties/methods.</p>
<p>Anyway, moving on&#8230;</p>
<p><strong>filter()</strong></p>
<p>With OS X 10.4, Apple introduced the NSArray <em>filteredArrayUsingPredicate:</em> method, which allows one to filter an array based on criteria established by an NSPredicate. Observe:</p>
<p><code>NSArray *arr = [NSArray arrayWithObjects: @"This", @"is", @"the", @"first", @"CenterStage", @"release", @"I", @"helped", nil];</code><br />
<code>arr = [arr filteredArrayWithPredicate: [NSPredicate predicateWithFormat: @"SELF.length &gt; 5"]]; // arr is now [@"CenterStage", "@"release", @"helped"]</code></p>
<p>Verbose, but useful.</p>
<p><strong>reduce()</strong></p>
<p>Frankly, Apple don&#8217;t give us any way to do this in pure Cocoa. The best way I&#8217;ve found (if you really need this, which is less often than one needs map() and filter()) is to use the F-Script framework and apply the \ operator to an array. Unfortunately, this takes a bit of overhead.</p>
<p>In conclusion, Cocoa and Objective-C almost bring us the joys of functional programming. We can only hope that Leopard and Obj-C 2.0 improve on these in some way.</p>
<p><a href="http://en.wikipedia.org/wiki/List_comprehension#In_Python">List comprehensions</a>, anyone?</p>
]]></html></oembed>