89

In python, you can do this:

[([None] * 9) for x in range(9)]

and you'll get this:

[[None, None, None, None, None, None, None, None, None],
 [None, None, None, None, None, None, None, None, None],
 [None, None, None, None, None, None, None, None, None],
 [None, None, None, None, None, None, None, None, None],
 [None, None, None, None, None, None, None, None, None],
 [None, None, None, None, None, None, None, None, None],
 [None, None, None, None, None, None, None, None, None],
 [None, None, None, None, None, None, None, None, None],
 [None, None, None, None, None, None, None, None, None]]

How can I do the equivalent in javascript?

priestc
  • 33,060
  • 24
  • 83
  • 117

18 Answers18

116

Array.fill

Consider using fill:

Array(9).fill().map(()=>Array(9).fill())

The idea here is that fill() will fill out the items with undefined, which is enough to get map to work on them.

You could also fill directly:

Array(9).fill(Array(9))

(important note: Array(9).fill(Array(9)) will fill each row of the array with the same array, so changing one row will change the other rows).

Alternatives to Array(9).fill() include

Array(...Array(9))
[].push(...Array(9))
[].concat(Array(9))
Array.from(Array(9))

We can rewrite the solution a bit more semantically as:

function array9() { return Array(9).fill(); }
array9().map(array9)

or

function array(n) { return Array(n).fill(); }
array(9).map(() => array(9))

Array.from provides us with an optional second mapping argument, so we have the alternative of writing

Array.from(Array(9), () => Array.from(Array(9));

or, if you prefer

function array9(map) { return Array.from(Array(9), map); }
array9(array9);

For verbose description and examples, see Mozilla's Docs on Array.prototype.fill() here.
and for Array.from(), here.

Note that neither Array.prototype.fill() nor Array.from() has support in Internet Explorer. A polyfill for IE is available at the above MDN links.

Partitioning

partition(Array(81), 9)

if you have a partition utility handy. Here's a quick recursive one:

function partition(a, n) {
  return a.length ? [a.splice(0, n)].concat(partition(a, n)) : [];
}  

Looping

We can loop a bit more efficiently with

var a = [], b;
while (a.push(b = []) < 9) while (b.push(null) < 9);

Taking advantage of the fact that push returns the new array length.

MPA
  • 1,878
  • 2
  • 26
  • 51
  • 2
    Very thorough. Nice response! – jamesplease Oct 12 '15 at 12:42
  • 1
    Note: If you want to fill the array with a certain number, e.g. 0, you can use `var a = Array(rows).fill(Array(columns).fill(0));`. [Example](https://i.imgur.com/uZVn9cl.png) – FatalMerlin Oct 17 '17 at 14:04
  • 59
    Note that using `Array(9).fill(Array(9))` will set each row to the _same_ array, meaning changing one row will affect the other rows. See this CodePen as an example: https://codepen.io/anon/pen/bMxjaG?editors=0012 – givemesnacks May 16 '18 at 00:39
  • 1
    @givemesnacks is right... it's useless if you want to write each element afterwards with a specific x, y because they all change ... – FluffyBeing Nov 15 '19 at 05:26
  • 7
    An edit should be made, because `Array(9).fill(Array(9))` is a really dangerous piece of code.. – Tofandel May 13 '20 at 14:00
  • 1
    Try to Avoid `Array(9).fill(Array(9))` ! It passes the shallow copy to each row, if changing any item inside, it will change all columns. – Jaden Oct 28 '20 at 16:10
  • yup `Array(9).fill(Array(9))` is not equivalent to `Array(9).fill().map(()=>Array(9).fill())` . Shallow copy. – devgeek May 28 '22 at 00:20
93
var matrix = [];
for(var i=0; i<9; i++) {
    matrix[i] = new Array(9);
}

... or:

var matrix = [];
for(var i=0; i<9; i++) {
    matrix[i] = [];
    for(var j=0; j<9; j++) {
        matrix[i][j] = undefined;
    }
}
Richard JP Le Guen
  • 28,364
  • 7
  • 89
  • 119
  • 4
    That's not empty though. just replace the inner loop with `matrix[j] = new Array(9)` – Esailija Nov 28 '11 at 19:49
  • 4
    There is something vaguely unsatisfying intellectually about using two different paradigms to create an array, first a loop, then a constructor. –  Oct 12 '15 at 11:27
  • 1
    Sure, this works, and may be the best solution. But somehow it seems to be missing the spirit of the question, which I interpreted as being "is there some **equivalently compact and elegant** way to do this". A little bit surprised that an answer that nests two loops to fill out a 2d array would both be upvoted so heavily and also accepted. –  Oct 12 '15 at 11:49
37

There is something about Array.fill I need to mention.

If you just use below method to create a 3x3 matrix.

Array(3).fill(Array(3).fill(0));

You will find that the values in the matrix is a reference.

enter image description here


Optimized solution (prevent passing by reference):

If you want to pass by value rather than reference, you can leverage Array.map to create it.

Array(3).fill(null).map(() => Array(3).fill(0));

enter image description here

HectorGuo
  • 1,011
  • 12
  • 9
15

// initializing depending on i,j:
var M=Array.from({length:9}, (_,i) => Array.from({length:9}, (_,j) => i+'x'+j))

// Print it:

console.table(M)
// M.forEach(r => console.log(r))
document.body.innerHTML = `<pre>${M.map(r => r.join('\t')).join('\n')}</pre>`
// JSON.stringify(M, null, 2) // bad for matrices

Beware that doing this below, is wrong:

// var M=Array(9).fill([]) // since arrays are sparse
// or Array(9).fill(Array(9).fill(0))// initialization

// M[4][4] = 1
// M[3][4] is now 1 too!

Because it creates the same reference of Array 9 times, so modifying an item modifies also items at the same index of other rows (since it's the same reference), so you need an additional call to .slice or .map on the rows to copy them (cf torazaburo's answer which fell in this trap)

note: It may look like this in the future, with Number.range() proposal

const M = [...Number.range(1,10)].map(i => [...Number.range(1,10)].map(j => i+'x'+j))
caub
  • 2,709
  • 2
  • 28
  • 31
4

This is an exact fix to your problem, but I would advise against initializing the matrix with a default value that represents '0' or 'undefined', as Arrays in javascript are just regular objects, so you wind up wasting effort. If you want to default the cells to some meaningful value, then this snippet will work well, but if you want an uninitialized matrix, don't use this version:

/**
* Generates a matrix (ie: 2-D Array) with: 
* 'm' columns, 
* 'n' rows, 
* every cell defaulting to 'd';
*/
function Matrix(m, n, d){
    var mat = Array.apply(null, new Array(m)).map(
        Array.prototype.valueOf,
        Array.apply(null, new Array(n)).map(
            function() {
               return d;
            }
        )
    );
    return mat;
}

Usage:

< Matrix(3,2,'dobon');
> Array [ Array['dobon', 'dobon'], Array['dobon', 'dobon'], Array['dobon', 'dobon'] ]

If you would rather just create an uninitialized 2-D Array, then this will be more efficient than unnecessarily initializing every entry:

/**
* Generates a matrix (ie: 2-D Array) with: 
* 'm' columns, 
* 'n' rows, 
* every cell remains 'undefined';
*/
function Matrix(m, n){
    var mat = Array.apply(null, new Array(m)).map(
        Array.prototype.valueOf,
        new Array(n)
    );
    return mat;
}

Usage:

< Matrix(3,2);
> Array [ Array[2], Array[2], Array[2] ]
dobon
  • 79
  • 4
4

If you really like one-liners and there is a use for underscore.js in your project (which is a great library) you can do write-only things like:

_.range(9).map(function(n) {
      return _.range(9).map(function(n) {
            return null;
      });
});

But I would go with standard for-cycle version mentioned above.

kubetz
  • 8,485
  • 1
  • 22
  • 27
  • I think you're missing a paren somewhere.. jslint likes this slightly modified version _.range(16).map(function (x) { return _.range(16).map(function (y) { return null }); }); }); – synthesizerpatel Nov 23 '13 at 12:02
  • you don't need the inner `_.range(9).map...`, it can be replaced with `new Array(9)` – pomber Jul 09 '15 at 22:29
  • @pomber try `new Array(9).map(i=>1)` in you console – caub Jan 18 '16 at 17:07
1

better. that exactly will work.

let mx = Matrix(9, 9);

function Matrix(w, h){
    let mx = Array(w);
    for(let i of mx.keys())
        mx[i] = Array(h);
    return mx;
}


what was shown

Array(9).fill(Array(9)); // Not correctly working

It does not work, because all cells are fill with one array

1

For a 2-d matrix I'd do the following

var data = Array(9 * 9).fill(0);
var index = (i,j) => 9*i + j;
//any reference to an index, eg. (3,4) can be done as follows
data[index(3,4)]; 

You can replace 9 with any generic ROWS and COLUMNS constants.

Davide C
  • 181
  • 1
  • 5
1

const dim1 = 9
const dim2 = 9
const init = 0
const matrix = Array.from({length:dim1}, _=>Array(dim2).fill(init))
priolo priolus
  • 266
  • 5
  • 7
1

The question is slightly ambiguous, since None can translate into either undefined or null. null is a better choice:

var a = [], b;
var i, j;
for (i = 0; i < 9; i++) {
  for (j = 0, b = []; j < 9; j++) {
    b.push(null);
  }
  a.push(b);
}

If undefined, you can be sloppy and just don't bother, everything is undefined anyway. :)

Amadan
  • 191,408
  • 23
  • 240
  • 301
0

Here's one, no looping:

(Math.pow(10, 20)+'').replace((/0/g),'1').split('').map(parseFloat);

Fill the '20' for length, use the (optional) regexp for handy transforms and map to ensure datatype. I added a function to the Array prototype to easily pull the parameters of 'map' into your functions.. bit risky, some people strongly oppose touching native prototypes, but it does come in handy..

    Array.prototype.$args = function(idx) {
        idx || (idx = 0);
        return function() {
            return arguments.length > idx ? arguments[idx] : null;
        };
    };

// Keys
(Math.pow(10, 20)+'').replace((/0/g),'1').split('').map(this.$args(1));
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]

// Matrix
(Math.pow(10, 9)+'').replace((/0/g),'1').split('').map(this.$args(1)).map(this.$args(2))
  • I'm not getting this. Where is the `this` in `this.$args(1)` given as the callaback to `map` supposed to come from? –  Oct 12 '15 at 11:31
0

JavaScript doesn’t have a built-in 2D array concept, but you can certainly create an array of arrays.

function createMatrix(row, column, isEmpty) {
        let matrix = []
        let array = []
        let rowColumn = row * column
        for (let i = 1; i <= rowColumn; i++) {
            isEmpty ?  array.push([]) :  array.push(i)

            if (i % column === 0) {
                matrix.push(array)
                array = []
            }
        }
        return matrix
    }

createMatrix(5, 3, true)

or

function createMatrix(row, column, from) {

        let [matrix, array] = [[], []],
            total = row * column

        for (let element = from || 1; element <= total; element++) {
            array.push(element)
            if (element % column === 0) {
                matrix.push(array)
                array = []
            }
        }

        return matrix
    }

createMatrix(5, 6, 1)
Driton Haxhiu
  • 117
  • 1
  • 6
0

Use this function or some like that. :)

function createMatrix(line, col, defaultValue = 0){ 
    return new Array(line).fill(defaultValue).map((x)=>{ return new Array(col).fill(defaultValue); return x; }); 
}
var myMatrix = createMatrix(9,9);
0

This seems to produce the desired result, as shown in the OP, without creating references:

function Matrix(x,y) {
  const mtrx = [];
  for (let i = 0; i < x; i++) {
    mtrx[i] = new Array(y).fill(null);
  }
  return mtrx;
}

const matrix = Matrix(9,9);

console.log(matrix);
chillywilly
  • 405
  • 3
  • 11
0

Well, you can create an empty 1-D array using the explicit Array constructor:

a = new Array(9)

To create an array of arrays, I think that you'll have to write a nested loop as Marc described.

Bob Gilmore
  • 12,608
  • 13
  • 46
  • 53
0

Coffeescript to the rescue!

[1..9].map -> [1..9].map -> null

david
  • 17,925
  • 4
  • 43
  • 57
-1

I'll give it my shot as well

var c = Array;

for( var i = 0, a = c(9); i < 9; a[i] = c(9), i++ );

console.log( a.join(",") );
//",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"

Readable and maintainable !

Esailija
  • 138,174
  • 23
  • 272
  • 326
-2

You can add functionality to an Array by extending its prototype object.

Array.prototype.nullify = function( n ) {
    n = n >>> 0;
    for( var i = 0; i < n; ++i ) {
        this[ i ] = null;
    }
    return this;
};

Then:

var arr = [].nullify(9);

or:

var arr = [].nullify(9).map(function() { return [].nullify(9); });
RightSaidFred
  • 11,209
  • 35
  • 35
  • @Tobo It ensures it's a non-negative and casts to an int at the same time. If n is "-1" or -1 it'll become 4294967295. It's what I'd consider incorrect behavior as an exception being thrown or an empty result being returned is how most of the standard JS functions would handle this. It should ideally be if (typeof n !== "number" || n < 0 || isNaN(n)) { throw exception or return undefined/[] here }. Also, it's generally an anti-pattern to prototype your methods into the built in JS objects. Google on that subject if interested. – Joseph Lennox Apr 30 '14 at 17:59