Array comprehensions

The first time I saw list comprehensions in Python, I was amazed of how fast they performed with a large number of iterations. Essentially, they give us a way to rewrite a simple loop in a single line. In Python, millions of simple computations could happen just in a matter of seconds. Until recently I didn't realize that JavaScript has array comprehensions (since v1.7), maybe because I was mainly looking at older libraries whose goal was wider browser support. But JavaScript has now borrowed some of the things I liked in Python. It even supports destructuring that allows for element swapping in a single operation instead of three, which can be useful for algorithms that make use of swapping.

I decided to create a short test of array comprehensions to see what they can be good at.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50function range(min,max,step) {
    var i, arr = [];
    if(!step) {step = 1;}
    for( i = min; i <= max; i += step ) {
        arr[i] = i;
    }
    return arr;
}
var r = range; // store a reference
var range = range(1,10);

var a = [x*x for(x in range) if(x % 2 == 0)]; // array comprehension
console.log(a); // [4,16,36,64,100]

var b = [];
for(var x = 1; x <= range.length; x++ ) {
    if(x % 2 == 0) {
        b[b.length] = x*x; // or b.push(x*x);
    }
}
console.log(b); // [4,16,36,64,100]

var c = range.map(function(x) {return x*x;}).filter(function(x) {return x % 2 == 0;});
console.log(c); // [4,16,36,64,100]

// All 3 variants led to the same result

// Now try to calculate geometric series with array comprehension
function sum(arr) {
    var sum = 0;
    for( var i = 0; i < arr.length; i++ ) {
        sum += arr[i];
    }
    return sum;
}

var fractions = [ 1/(Math.pow(2,x)) for(x in range)]; // 1/2 + 1/4 + 1/8 + ...
console.log(sum(fractions)); // 0.9990234375

var fractions = [ 1/(Math.pow(2,x)) for(x in r(1,30))];
console.log(sum(fractions)); // 0.9999999990686774

var fractions = [ 1/(Math.pow(2,x)) for(x in r(1,30,2))]; // 1/2 + 1/8 + 1/32 + ...
console.log(sum(fractions)); // 0.666666666045785

var fractions = [ Math.pow(2,x)/Math.pow(3,x) for(x in r(1,30))]; // 2/3 + 4/9 + 8/27 + ...
console.log(sum(fractions)); // 1.9999895698098988

var fractions = [ Math.pow(4,x)/Math.pow(7,x) for(x in r(1,30))]; // 4/7 + 16/49 + 64/343 + ...
console.log(sum(fractions)); // 1.3333332651313072

First I defined the function range that populates an array with the numbers from min to max in a step increment. If no step is provided, it is assumed to be 1. Then the variable range is used to store the resultant array. Then I show three simple ways to compute an array with quadratic numbers in it: through array comprehension, loop and a combination of map and filter functions. At the end I explore variants of calculating different geometric series, whose results we already know from maths. As we can see, results approximate certain numbers and with the increase in the size of the input array, we become even more accurate.

Of course, we shouldn't forget that JavaScript isn't the best language to work with floating point numbers, because this can be inaccurate. If we need to be more precise, we might still want to consider Python, because it was specifically designed for scientific accuracy.

Array comprehensions can have many uses and can make our code more readable when used in the right way. Browsers already support them, so we don't necessarily need to write more verbose versions of the same thing, when users of older browsers (more than two versions less than the current) are only few and will probably upgrade soon anyway. We always need to find ways to use the latest browser features, simply because this gives us more and more interesting opportunities. As with anything new, we need to find better uses of it and not just apply it, simply because it's trendy.

bit.ly/159PKdk