Going Functional: Map

7 January 2013 by Tomas Brambora - Category: Technical Articles

Let’s face it: the ‘map’ function (a.k.a. ‘apply-to-all’) is pretty simple. You just pass it an array and a function and it returns an array of results of that function applied to each item.

What’s it all about then? Here comes the important bit: the new array will have the same size as the original one. Always. In other words, the map function defines an ‘N -> N’ mapping. If you see it used, you immediately know the author wants a homomorphism.

Let’s see an example, removing trailing whitespace from strings in a list:

cleanStrings = _.map dirtyStrings, (str) -> str.trim()

Simple enough, right? Still, there are two things about ‘map’ I’d like to point out.

‘map’ vs ‘each’

Occasionally, you might see code like this:

_.map list, (obj) -> obj.doStuffWithSideEffectsAndNoReturnValue()

 

http://www.deshow.net/d/file/animal/2009-07/cute-kitten-631-2.jpg

Adorable kitten.

 

In this case, map is used for the side effect only. This is no good. Seriously. You’re killing an adorable kitten (see above) when you use map like this. Don’t do it. Also, you’re depriving yourself (and anyone who’s ever going to read your code) of one of the biggest advantages of the functional style: clear expression of intent.

If you see a ‘map’ you should immediately know the programmer wants to transform an array into another array of the same size. If you need to call a function for each item in a list just to trigger its side effect, use ‘each’ instead (just remember to be careful with side effects).

map vs list comprehensions

If you’re lucky enough to write in a language that sports list comprehensions, you can use those to achieve the same result as map.

cleanStrings = (str.trim() for str in dirtyStrings)

This is pretty neat too. However, there’s a caveat. Comprehensions are more powerful than that, in that they also allow you to filter out some of the items.

Example:

cleanStrings = (s.trim() for s in dirtyStrings when s.length > 2)

So when to use comprehensions and when map?

Well, that is a tough one. When I code in Python I use comprehensions all the time (mainly because of the way anonymous functions are defined in Python, which makes map kind of awkward to use). JavaScript doesn’t have them at all, so ‘map’ it is. In CoffeeScript, which has both, I tend to favor map if I’m doing a N -> N transformation and comprehensions when I need to filter some items out. But as always, use what feels natural to you.

Next in the series comes one of my favorite higher-order functions: reduce. Stay tuned.


« - »
COMMENTS
  • Matt

    You should take a look at the proposed ECMAScript array comprehensions

    • Tomas Brambora

      Thank for the comment. I know about those but unfortunately adding syntax to JS is a very complicated thing (I’m looking at you, IE :-) ). It will take years till comprehensions are implemented in the major browsers.

  • http://twitter.com/its_russ Russ

    Can’t you use _.filter() to accomplish the same thing as a list comprehension?

    • Tomas Brambora

      Hm, I don’t think so, at least not just by itself. Comprehensions are basically _.filter combined with a _.map. The point I was trying to make was more like whether it’s better to use a language construct or a library function to achieve the same thing.

  • Bill
  • Igor

    thanks for good paper, what about this thing: how can I stop processing if something bad happans ie:

    var newArray = _map.(array, function(item) {
    if (bad(item)) {
    stop_this_cycle;
    }
    return process(item);
    }

    • Tomas Brambora

      Hi Igor,

      the point I was making is that you shouldn’t do that. If you use a map, you are transforming the whole set of N items to a different set of N items. But always N -> N.

      See https://github.com/documentcloud/underscore/issues/211

      Rule of thumb would be: if you want to break early, use a for-cycle.

    • Matthew Gertner

      If it’s really “something bad” then throwing an exception is probably the appropriate response.