3

I was trying out a question on Hackerrank where I needed to create an array of array (basically 2d arrays).

My go-to one liner would be const counter = new Array(4).fill([]) However, I realized that it would create a 2D array but applying any function to the array would cause it to apply to all the elements.

let count = new Array(4).fill([])
count[0].push("Test")
console.log(JSON.stringify(count))

The outcome would be all the sub-arrays having the same value of "Test" inside them.

The final solution would be:

let count = Array.from(Array(4), () => new Array());
count[0].push("Test")
console.log(JSON.stringify(count))

May I ask the reason why it's not working as expected?

Ele
  • 33,468
  • 7
  • 37
  • 75
MrYanDao
  • 1,253
  • 1
  • 15
  • 27
  • 4
    I am not getting what is the issue you are having? – palaѕн Feb 25 '20 at 13:34
  • 3
    If you have the solution, then what's the problem now? – Ele Feb 25 '20 at 13:34
  • Because you're filling all the arrays with the reference to the same array. – Roberto Zvjerković Feb 25 '20 at 13:35
  • `.fill([])` will set all elements in the array to a reference to the array passed into fill, whereas with array.from, you're mapping each element to its own array instance – Nick Parsons Feb 25 '20 at 13:36
  • The question is the answer. Arrays are referenced, not copied, when passed around. – Wyck Feb 25 '20 at 13:38
  • Object's in JS are referenced, array happen to be objects too. So `var a = {x: 1}, b = a; b.x = 2;` Here `a.x` will end up being 2. Referenced object in JS can be tricky, but once you understand that one-liner, it becomes a lot clearer. – Keith Feb 25 '20 at 13:45

4 Answers4

5

Because .fill() takes the argument and, if it's an object, it copies the reference to that object into every index of the new array. Since in fact [] is an object, your new array ends up being filled with references to the same array.

From the docs:

If the first parameter is an object, it will copy its reference and fill the array with references to that object.

Sebastian Kaczmarek
  • 8,120
  • 4
  • 20
  • 38
2

When you use Array(4).fill([]), you are actually filling count with only one array ([]). All the elements inside count refer to the same array. This is why when you alter the first element (which is also referenced by the second, third, and fourth element) and print count, count's elements all have the same array object. The same referencing scenario in fill happens to all objects, not just arrays.

Here, you can see that the second, third, and fourth element refers (denoted by /**ref:2**/) to the object with the same id (/**id:2**/) as the first element:

let count = new Array(4).fill([])
count[0].push("Test")
console.log(count)
Richard
  • 7,037
  • 2
  • 23
  • 76
2
        .fill([]) | New Array
       +----------+------+------+------+------+
       |          |      |      |      |      |
Memory |    []    |indx0 |indx1 |indx2 |indx3 |
       |          |      |      |      |      |
       +----------+------+------+------+------+
            ^        |      |      |      |   
            |        |      |      |      |   
            +--------+      |      |      |   
            +---------------+      |      |
            +----------------------+      |
            +-----------------------------+

The four indexes are pointing to the same value in memory, so when you're pushing an element, you're doing on the same value (array) in memory.

Ele
  • 33,468
  • 7
  • 37
  • 75
1

Let me explain:

let count = new Array(4).fill([])

Here you create just 2 arrays actually. one is [] and second contains 4 links to [].

In other words you are creating an array of 4 links to the single instance of array.

--

let count = Array.from(Array(4), () => new Array());

Here you create 5 different arrays. 4 different empty [] and array that contains 4 links, one link to each unique [].

In other words here you create array filled with a different arrays

qiAlex
  • 4,290
  • 2
  • 19
  • 35