Initializing an array with values

[2013-11-23] dev, javascript, jslang, jsarrays
(Ad, please don’t block)
It is not a frequent use case, but it comes up occasionally: Producing an array [1] of a given length that is filled with values. This blog post explains how to do it and what to watch out for.

Let us start with something simple: producing an array of length n whose first element is 0, second element is 1, etc.

Array.prototype.map()

Once you have an array with length n, you can use the array method map() to fill it appropriately. For example, to produce the array [0, 1, 2], any array of length 3 will do:
    > var arr = [null, null, null];
    > arr.map(function (x, i) { return i })
    [ 0, 1, 2 ]
Alas, map() skips holes, but preserves them [2], which is why an empty array (with only holes) does not work for us:
    > new Array(3).map(function (x, i) { return i })
    [ , ,  ]
The next two sections look at ways of creating an array with a given length.

Filling an array via apply()

Function.prototype.apply() treats holes as if they were undefined elements [3]. Therefore, the following two expressions are equivalent.
    Array(undefined, undefined, undefined)
    Array.apply(null, Array(3))
If we combine this trick with map(), we get what we wanted:
    function fillArrayWithNumbers(n) {
        var arr = Array.apply(null, Array(n));
        return arr.map(function (x, i) { return i });
    }
The function in action:
    > fillArrayWithNumbers(5)
    [ 0, 1, 2, 3, 4 ]

_.range()

Underscore.js is the most popular way of complementing JavaScript’s Spartan standard library. It comes with the function range() which does what we want:
    > _.range(5)
    [ 0, 1, 2, 3, 4 ]
The previous trick with apply() is not very self-explanatory and Underscore.js is as close to a standard as it gets. Therefore, it is best to create filled arrays via _.range(). If we want to fill the array with something other than a range of numbers, we can use map():
    > _.range(3).map(function () { return 'a' })
    [ 'a', 'a', 'a' ]

Setting up multi-dimensional arrays

If you need multiple dimensions for elements, you must nest arrays. When you create such nested arrays, the innermost arrays can grow as needed. But if you want direct access to all elements, you need to create the outer arrays.

Let’s use _.range() to set up a matrix for Tic-tac-toe. We initialize all arrays correctly:

    var ticTacToe = _.range(3).map(function () {
        // Create one row
        return _.range(3).map(function () {
            return '.';
        });
    });
    ticTacToe[0][2] = 'X';  // [row][column]
    ticTacToe.forEach(function (row) {
        console.log(row.join(' '));
    });
Output:
    . . X
    . . .
    . . .

References

  1. Arrays in JavaScript
  2. Array iteration and holes in JavaScript
  3. Apply and arrays: three tricks