14

I face something I don't understand with an array. Indeed, I created an array I have filled with empty subArrays to obtain a 2D Matrix. But when I manipulate the array it doesn't behave as I expected.

var arr = new Array(5);
arr.fill([]);
arr[2].push("third rank item");
console.log(arr);

//[ [ 'third rank item' ],
//  [ 'third rank item' ],
//  [ 'third rank item' ],
//  [ 'third rank item' ],
//  [ 'third rank item' ] ]

Every lights on this matter will be welcomed

5
  • How do you expect it to behave? Commented Dec 13, 2016 at 13:12
  • 2
    You're filling with the same empty array. You could instead try Array.from(new Array(5), () => []). Commented Dec 13, 2016 at 13:12
  • @torazaburo Can you explain it briefly? OP targeted the 3rd item in the outer array and pushed a string that results in pushing the string in all the internal arrays. Bit confusing. Commented Dec 13, 2016 at 13:14
  • All the internal arrays are the same array. Commented Dec 13, 2016 at 13:16
  • All the internal arrays are the same array. Commented Dec 13, 2016 at 13:16

9 Answers 9

23

This is the same old problem with arrays (and objects in general) being references rather than values.

Specifically, when you do arr.fill([]), you are taking that one single empty array and using that to fill the parent one.

It's like saying:

var arr = new Array(5);
arr[0] = arr[1] = arr[2] = arr[3] = arr[4] = [];

They all refer to the same array! So when you then go on to modify one of them, it looks like they're all modified (but really it's still the same one)

Unfortunately there's no simple way to assign an empty array to each one. You could do something like:

Array.apply(null, Array(5)).map(function() {return [];});

Essentially, create an (initialised) empty array of length 5 and map each (empty) value to a new [].

EDIT: Seems like I'm stuck in old times. As per @torazaburo's comment, you can use Array.from instead of Array.apply(null, Array(5)).map, like so:

Array.from( new Array(5), function() { return []; } );
Sign up to request clarification or add additional context in comments.

1 Comment

let arr = Array.from(Array(n), () => []); where n would be 5 in this instance
2

As you can notice using array.fill you're filling the array with a reference to the same array,

if you want to instantiate each array index to an empty array a normal while loop will do:

var arr = [];
var n = 5
while(n--)
  arr[n] = []

arr[2].push("third rank item");
console.log(arr);

Option 2:
if you have lodash package available, you can also use _.map as this is specificaly designed to loop through a sparse array (native map will skip non init values)

var arr =_.map(new Array(5), (x => []))

arr[2].push("third rank item");
console.log(arr)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.2/lodash.min.js"></script>

Comments

2

The eleventh line of the ECMA doc of Array.prototype.fill is clearly giving the reason for the mystery.

Repeat, while k < final

Let Pk be ToString(k).

Let setStatus be Set(O, Pk, value, true).

ReturnIfAbrupt(setStatus).

Increase k by 1.

Here "value" is just a reference received. And they are setting it as a property to array directly. That means all the filled arrays are just reference to a single array object.

Comments

2

It's happens cause of reference. Array is a type of object and object works on their references when you fill your array with [] or new Array() fill run only ones and put the same array in all indexes that's why when you update an sub-array all are updated.

Solution:

let arr = new Array(5).fill(0).map(ele => ele = []); arr[2].push("something");

OR

let arr = Array.of([], [], [], []); arr[2].push("something");

Result: as expected only 2 index of arr is updated.

Comments

1

With ES6 I recommend this method to create 2 or multidimensional arrays:

// create an M x N dimension grid and fill it with 0's
const myArray = [...Array(M)].map(r => [...Array(N)].map(r => 0));

Comments

0

Try this ,this is quick solution for you in one line.

var arr = new Array(5);
arr = Array.from(arr, x => []);
arr[2].push("third rank item");
console.log(arr);

Comments

0

You can try this,

var arr = new Array(5);
var i = 0;
while (i < arr.length)
  arr.fill([], i++);
arr[2].push("third rank item");
console.log(arr);

Comments

0

A bit different:

let array= JSON.parse(JSON.stringify(new Array(N).fill([])));

or

let array=new Array(N).fill(null).map(()=>[]);

Comments

-1

All the answers were correct and reasonable and so I decided to just try it out on my Chrome console. So the answer to the OP is that this is happening because you cannot do a direct fill.

If I understand part of the question is why can't you just do:

const grid = Array(5).fill([])

which is saying okay we have a structure like this:

//[ [ ],
//  [ ],
//  [ ],
//  [ ],
//  [ ] ]

And then you are saying go and fill the third one like so:

grid[2].push("third rank item");

But instead of seeing the third one filled with that string, you see all of them. It's simple, you are creating one single array and throwing it in at every location inside of grid. In memory there is only one inner array so you threw that string into that array, it would affect every index.

But I found some behavior slightly different than what I had expected.

If you write the following:

const grid = Array(3).fill([false, false, false]);

You would get the following:

(3) [Array(3), Array(3), Array(3)]
(3) [false, false, false]
(3) [false, false, false] 
(3) [false, false, false]

Then if you run: grid[0].push(true), you will get the following:

(3) [Array(4), Array(4), Array(4)]
(4) [false, false, false, true]
(4) [false, false, false, true]
(4) [false, false, false, true]

So modifying one, modifies all, so that's why you cant just do a direct fill and then push as you did, instead you have to do the map() statement like so:

const grid = Array(3)
  .fill(null)
  .map(() => Array(3).fill(false));

And that will run the inner function three times and each time we generate a brand new and different array.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.