Why do changes made to a cell propagate to other c

2020-02-12 03:04发布

问题:

I have a problem with this 2-dimensional array in JS. When I change a[1][0], a[0][0] changes with it. Is there something wrong in the way I am initializing it? If yes, how can I initialize it properly?

>var a = Array(100).fill(Array(100).fill(false));
>a[0][0]
>false
>a[1][0]
>false
>a[1][0] = true
>true
>a[1][0]
>true
>a[0][0]
>true

回答1:

var a = Array(100).fill(Array(100).fill(false));

a contains an array, each element of which references to an array. you are filling the outer array with an array which contains all false values. The inner array is being made only once and reference to the same array is passed to each element of outer array that is why if you perform an operation on one element it reflects on other elements as well.

This is actually equivalent to

var a1 = Array(100).fill(false);
var a = Array(100).fill(a1);

here a gets 100 elements all having reference to same array a1. So if you change one element of a, all elements change since they are references to same array.

you will need to fill each element in outer array with a new array. you can do something like this:

var a = [];
for(var i=0; i<100; i++)
  a.push(Array(100).fill(false));


回答2:

Your entire array will be filled with references to the same (second dimension) array object.

To fill it with distinct objects, you'd have to do something like this:

const a = Array(100).fill(false).map(x => Array(100).fill(false));

a[0][0] = true;

console.log(a[0][0]);
console.log(a[0][1]);
console.log(a[1][1]);

Note that the values in the initial array need to be explicitly set to something (in my example false, but could also be undefined), because the array created by the constructor is sparse and the map() function will only operate on properties that actually exist.

To work around that, you could use Array.from():

const a = Array.from(Array(100), x => Array(100).fill(false));

a[0][0] = true;

console.log(a[0][0]);
console.log(a[0][1]);
console.log(a[1][1]);



回答3:

Your problem is that you are using the second Array(100).fill(false) in every position of the first array. Thats why when you change one value in the "second" dimension it will change in every single position of the array. If you want to avoid this you hve to create a new Array for each position of the initial.

var x = Array(100).fill().map(x => Array(100).fill(false));


回答4:

what you are doing here is adding the same array object at each index of bigger array.

So when you do

Array(x).fill(Array(y).fill(false));

What you are doing actually is :

Array(x).fill(Y); // i.e. X.fill(Y)

Now whenever you change Y, you will get the same value at each index of X.

You should use loop to fill in data when data itself is an object(remember Array is object).



回答5:

These looks like a duplicate question but I've always just used something like this

var x = new Array(10);
for (var i = 0; i < 10; i++) {
  x[i] = new Array(10).fill(false);
}
x[5][5] = true

Credit: How can I create a two dimensional array in JavaScript?



回答6:

Since arrays are reference types, creating an N Dimensional array in JS is not so trivial. You need to create a clone tool first.

Array.prototype.clone = function(){
  return this.map(e => Array.isArray(e) ? e.clone() : e);
};

function arrayND(...n){
  return n.reduceRight((p,c) => c = (new Array(c)).fill().map(e => Array.isArray(p) ? p.clone() : p ));
}

var arr = arrayND(2,3,4,"x")
console.log(JSON.stringify(arr));

arrayND takes indefinite number of integer arguments each designating the size of a dimension and the last argument is the fill value.



回答7:

The fill function as described in mdn, copies the same value to all array indices. You can use the below utility to create clones of the array. But since i am using the slice operator, this is restricted to cloning arrays.

https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/fill

   if (!Array.prototype.clonefill) {
  Object.defineProperty(Array.prototype, 'clonefill', {
    value: function(value) {

      // Steps 1-2.
      if (this == null) {
        throw new TypeError('this is null or not defined');
      }

      var O = Object(this);

      // Steps 3-5.
      var len = O.length >>> 0;

      // Steps 6-7.
      var start = arguments[1];
      var relativeStart = start >> 0;

      // Step 8.
      var k = relativeStart < 0 ?
        Math.max(len + relativeStart, 0) :
        Math.min(relativeStart, len);

      // Steps 9-10.
      var end = arguments[2];
      var relativeEnd = end === undefined ?
        len : end >> 0;

      // Step 11.
      var final = relativeEnd < 0 ?
        Math.max(len + relativeEnd, 0) :
        Math.min(relativeEnd, len);

      // Step 12.
      while (k < final) {
        O[k] = value.slice(0);
        k++;
      }

      // Step 13.
      return O;
    }
  });
}
Array(100).clonefill(Array(100))
a[0][0] = true
a[1][0]
false