0

I faced a strange situation when I tried to create an array of arrays via fill and then I tried to add some value to the first child I saw all other nested arrays receive this value as well. I have an idea why it could happen. It looks like the fill property uses the same object of all 3 nested 'places\items' and now each item is the reference to the single Array.

Also, I tried to use the .fill(new Array()) instead of the literal expression but I had the same result.

I'm not sure if I'm right so fix me, please, if I missed something. Thanks

// an amount of the required output arrays
const requiredArrays = 3; 

const cols = new Array(requiredArrays).fill([]);
cols[0].push({id: 1})
 

The expected result:

[
    [
        {
            "id": 1
        }
    ],
    [],
    []
]

the actual result:

[
    [
        {
            "id": 1
        }
    ],
    [
        {
            "id": 1
        }
    ],
    [
        {
            "id": 1
        }
    ]
]

P.S. What is the right way to achieve the result I want to have? Should I just use the for cycle and populate the parent array via the children or maybe some nicer way exists?

Velidan
  • 5,526
  • 10
  • 48
  • 86
  • 1
    can you show the content of `requiredArrays`? [Here](https://jsfiddle.net/cLx9hr4m/) seems to be ok...maybe you're changing `cols` somewhere else and Arrays are mutable – Elmer Dantas Jun 28 '21 at 14:22
  • Hi @ElmerDantas, I updated the question. Sorry about the missed detail. – Velidan Jun 28 '21 at 14:32
  • Hi @Reyno thank you for the answer but I'm not sure if it's the same. In the example, you shown the author used a direct assignment of the source array ref to the variable thus we received a pointer to the source array in the second variable. In my case I didn't do it, I just wanted to create an array that contain 3 nested arrays and they should be different. That's why I used the **Fill** method to do it in the short way – Velidan Jun 28 '21 at 14:32
  • @ElmerDantas your example doesn't match OP's because `new Array(new Array())` only creates an array with one element in it. It's equivalent to `[ [] ]`. If you update the one and only element in an array, you do not get other elements updated. [Try it with OP's code](https://jsfiddle.net/un1zc54t/) – VLAZ Jun 28 '21 at 14:50

2 Answers2

2

From the fill docs at MDN:

If the first parameter is an object, each slot in the array will reference that object.

This means that you get an array of references to the same array (which is an object as well).

As to how to do it in a nicer way... that depends on what you want to achieve. Why do you need the array of arrays? Will they all have a fixed length?

bncpr
  • 56
  • 5
  • he is passing an empty array to `fill`, not an object. – Elmer Dantas Jun 28 '21 at 14:26
  • 2
    @ElmerDantas an array is [also an object](https://stackoverflow.com/questions/5048371/are-javascript-arrays-primitives-strings-objects) – Reyno Jun 28 '21 at 14:27
  • @ElmerDantas can you please try `typeof []` and report back what you get? – VLAZ Jun 28 '21 at 14:37
  • @bncpr thank you for your answer. Marked your answer as useful. Basically, I want to create something like a Masonry grid and create columns with UI items that should be sorted by height to use all available space without gaps. So I'll know the column amount I need to render but the lengths of these children (nested array) will be unknown but it doesn't matter. I'll just sort items via height and push them into these nested array – Velidan Jun 28 '21 at 14:44
  • @Reyno true, I keep forgetting about this :D – Elmer Dantas Jun 29 '21 at 07:28
1

Arrays are passed down by reference this makes it not that great for the fill method. Though you can fill it with a placeholder value and then map those to an array.

const requiredArrays = 3; 

const cols = new Array(requiredArrays).fill('').map(() => []);
cols[0].push({id: 1})

console.log(cols);

EDIT: As pointed out in the comments, a better solution:

const requiredArrays = 3; 

const cols = Array.from({length: requiredArrays}, () => []);
cols[0].push({id: 1})

console.log(cols);
Reyno
  • 6,119
  • 18
  • 27
  • Does it mean the fill method uses the same literal it receives at the moment of execution and uses it for all the children? So the children receive not a dedicated array but rather a reference (pointer) to the first one that the **fill** received at the start? (In this case each child uses the reference to the same array) – Velidan Jun 28 '21 at 14:35
  • Correct they all just get a pointer the the initial array. – Reyno Jun 28 '21 at 14:36
  • I haven't known the **fill** method works in this way. Marked your answer as the correct one. Thank you. – Velidan Jun 28 '21 at 14:38
  • 1
    `Array.from({ length: requiredArrays }, () => []}` is probably better than creating an array, filling it with throwaway values, then mapping over them. – VLAZ Jun 28 '21 at 14:38