Fast min/max in arrays

This morning while commuting to the office, reading JavaScript for Web Developers, I bumped into a question: how could I improve a well known algorithm that gets the smallest or the largest number in an array by using built-in Math methods such as Math.min and Math.max. For those not familiar with these methods, they return the smallest or the largest number respectively from a list of zero or more numbers passed as parameters, e.g.: Math.min(4,3,9,6) returns 3, Math.max(4,3,9,6) returns 9. I was wondering if calling such methods using apply and an Array as argument would work. I could hardly wait to get to the office to test it out on my Firebug console. I first tried:

Math.max.apply(Math, [4,8,3,5,1,2]);

And bingo! It works!

Previous work

Since it seemed so easy I thought that somebody else would had already figured this out before and I did a quick search for Math.max.apply and *bam!*: the first occurrence was a John Resig’s post back in 07’s where he describes such technique for augmenting Array type. Really handy.

Performance

My main concern to decide whether to use this technique was its performance, so I ran a quick test comparing it to a very popular for loop algorithm and an optimized loop for a given array of random numbers:

var i, max,
    a = [],
    len = 1e4;
 
/* create an array of ten thousand random numbers */
console.time('array creation');
for (i = 0; i < len; i += 1) {
    a[i] = Math.floor(Math.random() * 1e4);
}
console.timeEnd('array creation');
 
/* for loop */
max = -Infinity;
console.time('for loop');
for (i = 0; i < len; i += 1) {
    if (a[i] > max) {
        max = a[i];
    }
}
console.timeEnd('for loop');
console.log(max);
 
/* optimized loop */
max = -Infinity;
i = len;
console.time('optimized loop');
while (i--) {
    if (a[i] > max) {
        max = a[i];
    }
}
console.timeEnd('optimized loop');
console.log(max);
 
/* Math.max */
console.time('Math.max');
max = Math.max.apply(Math, a);
console.timeEnd('Math.max');
console.log(max);

Running this code on my firebug console (Firefox 3.0.14), I got the same largest number on each technique within the array as expected and the following times:

  • array creation: 89ms
  • for loop: 29ms
  • optimized loop: 20ms
  • Math.max: 1ms

Wow! This is pretty fast! Huh? You can copy the code above and paste on your firebug console in order to get your own results or you can check it out here.

Conclusion

This technique deserves lots of credit for its simplicity and speed, it’s those one-line codes that beats any fancy and complex algorithm that once you get to know, you start using every time you need, however it’s not true for all browsers :-(

3 Comments

  1. Karl says:

    That’s a quite an interesting snippet but for some reason I didn’t get the same results on FireFox 3.5(.3).

    For loop: 15 ms
    Optmized for: 13 ms
    Math.max: 22 ms

    So I decided to test this on some other browsers and IE 6 - 8 as well as Opera 10 showed significantly faster speeds on Math.max.apply. However the method does not work with Chrome or Safari.

  2. Marcel Duran says:

    Thanks Karl,

    The problem with Chrome and Safari is likely to be related to Webkit engine. That test page has a default array size of one million random numbers which is too much for those browsers. They complain either about apply not supporting one million arguments or maximum call stack size exceeded.
    I’ve updated that page adding a try/catch block showing the error message when error occurs. If you reduce the array size, let’s say to 100 thousand it works on those browsers but with a much worse performance.

  3. MarcusT says:

    I’m running FF 3.5.3 and also get disappointing results (virtually identical every time):

    Array creation:	161	ms
    For loop:	13	ms
    Optmized for:	14	ms
    Math.max:	21	ms

    Even so, the one liner is infinitely preferable in use to the alternatives.

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">