DEV Community

solring
solring

Posted on

[JS newbie] Array().fill() is marvellous but...don't do this.

OK, the tragedy was like this.

Everyone knows how convenient the javascript Array fill() is to let users initialize array data elegantly. Perfect invention.

// create an array of length 6 with all elements equal to 0.
let arr = Array(6).fill(0)
Enter fullscreen mode Exit fullscreen mode

It is even extremely useful if for creating a range of integers for iteration or some other fancy operations.

// you must fill the array or there won't be keys
Object.keys(Array(6).fill(0)).map((i) => dosomething(i));
Enter fullscreen mode Exit fullscreen mode

Recently I was fascinated by these kinds of functional programming styles and mistakenly assumed that is was similar to Python collections.defaultdict() which lets you pass a primitive or an object as a factory so that it will automatically new an independent value for each entry in the array.

Unfortunately, it is not the case.

fill(0) works perfectly fine since the argument is a primitive. However, if you pass something like an object {}, Every entry in the array will actually contain(or point to?) the same object. Every element will be the same.

That is to say, if you try to modify one of the element object(like, assign or change some value of a key), every single element will be modified as well, which is very unlikely to be your original intention.

> arr = Array(6).fill({})
[ {}, {}, {}, {}, {}, {} ]
> arr[3]['someKey'] = "hello"
'hello'
> arr
[
  { someKey: 'hello' },
  { someKey: 'hello' },
  { someKey: 'hello' },
  { someKey: 'hello' },
  { someKey: 'hello' },
  { someKey: 'hello' }
]
Enter fullscreen mode Exit fullscreen mode

Boom.

This problem is very similar a classical trap in Python when you try to initialize an array of array in this very intuitive but disastrous way:

>>> arr = [[0] * 3 ] * 3
>>> arr
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
>>> arr[1][1] = 777
>>> arr
[[0, 777, 0], [0, 777, 0], [0, 777, 0]]
Enter fullscreen mode Exit fullscreen mode

Here the first level [0] * 3 is fine, but the second multiply is actually multiplying the reference to the array [0] * 3, therefore changing one element in one array will lead to changing all of the corresponding element in other arrays (because they are the same reference!)

OK, everyone can start to laugh!

Top comments (2)

Collapse
 
stojakovic99 profile image
Nikola Stojaković • Edited

That's why you should always use map for scenarios like this:

// will fill array with 3 empty slots and then set a new object for each of these slots
let array = new Array(3).fill().map(() => ({}));

array[0].someKey = 'hello';

console.log(array);
Enter fullscreen mode Exit fullscreen mode

Output:

[
  { someKey: "hello" },
  {},
  {}
]
Enter fullscreen mode Exit fullscreen mode
Collapse
 
solring profile image
solring

Thanks for the tip! This is much more elegant.