Today I Learned

Generating JS multi dimensional arrays edge cases

tl;dr

Don’t use Array#fill to generate multi dimensional arrays

Use other methods instead, like this for example:

Array.from(
  { length: 3 },
  () => Array.from({ length: 3 })
)
Array(3).fill().map(
  () => Array(3).fill()
)

Long version

One of many ways to generate an array is to use JS Array constructor:

> a = Array(5)
[ <5 empty items> ]
> a.length
5

Array can be filled with some value using Array#fill method

> a = Array(5).fill('value')
[ 'value', 'value', 'value', 'value', 'value' ]
> a = Array(5).fill(0)
[ 0, 0, 0, 0, 0 ]
> a = Array(5).fill()
[ undefined, undefined, undefined, undefined, undefined ]
> a[1] = 42
42
> a
[ undefined, 42, undefined, undefined, undefined ]

So far it works fine, but be careful with filling array with a reference type, like an Array or Object

> b = Array(3).fill(Array(3).fill())
[ [ undefined, undefined, undefined ],
  [ undefined, undefined, undefined ],
  [ undefined, undefined, undefined ] ]
> b[1][1] = 42
42
> b
[ [ undefined, 42, undefined ],
  [ undefined, 42, undefined ],
  [ undefined, 42, undefined ] ]
> b[1] === b[2]
true

Each row of such generated array is actually the same object
It’s literally what the documentations says:

arr.fill(value[, start[, end]])
value: Value to fill the array with. (Note all elements in the array will be this exact value.)

MDN web docs
But not everybody has to know that, right?
And using Array#fill without that knowledge might lead to a hard to track bug in your application

Solutions

Fill the array with some value, and then map through the values

> a = Array(3).fill().map(() => Array(3).fill())
[ [ undefined, undefined, undefined ],
  [ undefined, undefined, undefined ],
  [ undefined, undefined, undefined ] ]
> a[1][1] = 42
42
> a
[ [ undefined, undefined, undefined ],
  [ undefined, 42, undefined ],
  [ undefined, undefined, undefined ] ]

Or use other method, like Array.from

> a = Array.from({ length: 3 }, () => Array.from({ length: 3 }))
[ [ undefined, undefined, undefined ],
  [ undefined, undefined, undefined ],
  [ undefined, undefined, undefined ] ]
> a[1][1] = 42
42
> a
[ [ undefined, undefined, undefined ],
  [ undefined, 42, undefined ],
  [ undefined, undefined, undefined ] ]
>