The extended map function

The map function I will present is the most useful map function you'll find. Why? Because it can replace most for loops! And replacing for loops will produce much cleaner JavaScript code.

What is a map function? Standard map function applies a function to a sequence of elements. Let's do a rewrite that will show you what I mean. A for loop could like this:

var list = [1, 2, 3]
for(var i=0; i < list.length; i++)
   myFn(list[i])

Rewritten using map the above loop will look like this:

map([1, 2, 3], my_fn)

Much cleaner don't you think?

The extensions

Most library's (JQuery, Prototype, MochiKit) implement a dumb map function, that acts as "the real map function", i.e. a map function that can only apply a function to a sequence of elements and nothing more. But I figured out there is no need to put such a limitation. The map function could be almost as powerful as a for loop!

My map function signature looks like this:

map(list, fn, /*optional*/ start_index, end_index)

Notice that you can specify a start_index and end_index, an example:

map(['A', 'm', 'i', 'r'], function(char) {
   alert(char)
}, 1, 3)

The above code will alert 'm', 'i'. The above example looks like this using a standard for loop:

var chars = ['A', 'm', 'i', 'r'];
for(var i=1; i < 3; i++) {
   alert(chars[i])
}

That's the first extension and you may find it quite useful in certain situations.

The second extension is, that you can get the current index, an example:

map(['A', 'm', 'i', 'r'], function(char, i) {
   alert(i + ':' + char);
});

The above example will alert 0:A, 1:m, 2:i, 3:r. This extension is also quite useful in certain situations.

The third extension is, that you can return from a map function - this is really useful. An example:

var is_i_found = map(['A', 'm', 'i', 'r'], function(char) {
   if(char == i)
      return true;
});
alert(is_i_found); //Alerts true

Anyway, the extensions may look like an overkill, but I can assure you that they are quite useful. In Todoist I only use around 6 for loops (and I have around 5000 lines of JavaScript), everything is handled by the extended map function!

The implementation

function map(list, fn,/*optional*/ start_index, end_index) {
    var i = 0, l = list.length;
    if(start_index)
         i = start_index;
    if(end_index)
         l = end_index;
    for(i; i < l; i++) {
        var val = fn.apply(null, [list[i], i]);
        if(val != undefined)
            return val;
    }
}

That's it. You'll find this map function in AJS (my JavaScript library).

A real world example

In Todoist it's possible to write a every last day recurring event. In order to implement this, one needs to know the last day of a month. Here is how I solve this using the extended map function:

function getLastDay(_date) {
    var clone = new Date(_date);
    var now_month = _date.getMonth();
    return map([31, 30, 29, 28], function(days) {
        now = new Date(clone);
        now.setDate(days);
        if(now_month == now.getMonth())
            return now;
    });
}

The above code takes a date as input and returns a new "last day" date object. Notice the usage of map.

Code · JavaScript · Tips 26. Apr 2007
© Amir Salihefendic. Powered by Skeletonz.