简体   繁体   中英

Strange behavior of an array filled by Array.prototype.fill()

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

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 []; } );

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.

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> 

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.

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); 

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); 

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));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM