Initialising multidimensional arrays in Javascript can be tricky because some initialisation approches lead to unexpected behaviour due to shallow copying.
2D Arrays (Matrices)
If we try to create a 4 \(\times\) 4 matrix by filling an empty array with 4 zero-filled arrays each with 4 zeros
using the Array(n).fill(e) syntax:
let arr = Array(4).fill(Array(4).fill(0));
console.table(arr);
┌─────────┬───┬───┬───┬───┐
│ (index) │ 0 │ 1 │ 2 │ 3 │
├─────────┼───┼───┼───┼───┤
│ 0 │ 0 │ 0 │ 0 │ 0 │
│ 1 │ 0 │ 0 │ 0 │ 0 │
│ 2 │ 0 │ 0 │ 0 │ 0 │
│ 3 │ 0 │ 0 │ 0 │ 0 │
└─────────┴───┴───┴───┴───┘
and then try to change the element \(arr_{1,2}\):
arr[1][2] = 1;
console.table(arr);
┌─────────┬───┬───┬───┬───┐
│ (index) │ 0 │ 1 │ 2 │ 3 │
├─────────┼───┼───┼───┼───┤
│ 0 │ 0 │ 0 │ 1 │ 0 │
│ 1 │ 0 │ 0 │ 1 │ 0 │
│ 2 │ 0 │ 0 │ 1 │ 0 │
│ 3 │ 0 │ 0 │ 1 │ 0 │
└─────────┴───┴───┴───┴───┘
All elements in column 2 (arr[0:3][2]) get mutated, which hardly would be what you want. It appears that the fill() method used shallow copies of [...Array(4).fill(0)] to fill the outer array.
A straightforward workaround is to serialise and de-serialise using JSON.stringify() and JSON.parse():
let arr = Array(4).fill(Array(4).fill(0));
arr = JSON.parse(JSON.stringify(arr));
arr[1][2] = 1;
console.table(arr);
┌─────────┬───┬───┬───┬───┐
│ (index) │ 0 │ 1 │ 2 │ 3 │
├─────────┼───┼───┼───┼───┤
│ 0 │ 0 │ 0 │ 0 │ 0 │
│ 1 │ 0 │ 0 │ 1 │ 0 │
│ 2 │ 0 │ 0 │ 0 │ 0 │
│ 3 │ 0 │ 0 │ 0 │ 0 │
└─────────┴───┴───┴───┴───┘
Now, only the single element arr[1][2] gets mutated.
We can also use the following syntax to directly initialise a matrix whose elements can be properly mutated:
let arr = Array.from({ length: 4}, e => Array(4).fill(0));
arr[1][2] = 1;
console.table(arr);
┌─────────┬───┬───┬───┬───┐
│ (index) │ 0 │ 1 │ 2 │ 3 │
├─────────┼───┼───┼───┼───┤
│ 0 │ 0 │ 0 │ 0 │ 0 │
│ 1 │ 0 │ 0 │ 1 │ 0 │
│ 2 │ 0 │ 0 │ 0 │ 0 │
│ 3 │ 0 │ 0 │ 0 │ 0 │
└─────────┴───┴───┴───┴───┘
Higher Dimensional Arrays
The shallow-copying problem related to Array(n).fill(e) chaining also happens in 3D arrays:
let arr = Array(3).fill(Array(3).fill(Array(3).fill(0)));
arr[1][1][2] = 1;
console.table(arr);
┌─────────┬─────────────┬─────────────┬─────────────┐
│ (index) │ 0 │ 1 │ 2 │
├─────────┼─────────────┼─────────────┼─────────────┤
│ 0 │ [ 0, 0, 1 ] │ [ 0, 0, 1 ] │ [ 0, 0, 1 ] │
│ 1 │ [ 0, 0, 1 ] │ [ 0, 0, 1 ] │ [ 0, 0, 1 ] │
│ 2 │ [ 0, 0, 1 ] │ [ 0, 0, 1 ] │ [ 0, 0, 1 ] │
└─────────┴─────────────┴─────────────┴─────────────┘
The same methods (JSON.parse(JSON.stringify(arr)) and Array.from()) can be used to properly initialising them.
let arr = Array.from(
{ length: 3 }, e => Array.from(
{ length: 3 }, e => Array(3).fill(0)));
arr[1][1][2] = 1;
console.table(arr);
┌─────────┬─────────────┬─────────────┬─────────────┐
│ (index) │ 0 │ 1 │ 2 │
├─────────┼─────────────┼─────────────┼─────────────┤
│ 0 │ [ 0, 0, 0 ] │ [ 0, 0, 0 ] │ [ 0, 0, 0 ] │
│ 1 │ [ 0, 0, 0 ] │ [ 0, 0, 1 ] │ [ 0, 0, 0 ] │
│ 2 │ [ 0, 0, 0 ] │ [ 0, 0, 0 ] │ [ 0, 0, 0 ] │
└─────────┴─────────────┴─────────────┴─────────────┘