最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

arrays - issue with calling a javascript function twice - Stack Overflow

programmeradmin1浏览0评论

I am trying to write a fairly simple Javascript function, and experiencing behavior I don't understand when I iterate the function.

I have distilled the problem down to the following situation. I want to write a function that will take as input an array consisting of arrays of arrays, e.g. A = [[[1]]]. I don't know the standard terminology for this, so I am going to refer to the main array as "level 0", which has elements that are arrays of "level 1". I'll say the level 1 arrays consist of arrays of "level 2". The level 2 arrays consist of integers.

The function does the following, on input A (a level 0 array):

  1. create an empty array L;
  2. for each level 1 array M in A
    • add one to each integer entry in each level 2 array in M;
    • add two copies of M to L
  3. return L.

Here is my code:

function myFunc(A){
  var L = [];
  for(var a=0; a<A.length; a++){
    var M = A[a].slice(0);
    for(var i=0; i<M.length; i++){
      for(var j=0; j<M[i].length; j++){
        M[i][j]++;
      }
    }
    for(var s=0; s<2; s++){
      var N = M.slice(0);
      L.push(N);
    }
  }
  return(L);
}

Now I test it out:

var A = [[[1]]];

A = myFunc(A)

After this, I get A = [[[2]],[[2]]], which is what I expect. However, suppose I iterate it:

var A = [[[1]]];

A = myFunc(A);

A = myFunc(A);

Then I expect to obtain A = [[[3]],[[3]],[[3]],[[3]]], but instead I have A = [[[4]],[[4]],[[4]],[[4]]].

On the other hand if I run myFunc([[[2]],[[2]]]), I do get the expected [[[3]],[[3]],[[3]],[[3]]].

I don't understand where this discrepancy is ing from.

I am trying to write a fairly simple Javascript function, and experiencing behavior I don't understand when I iterate the function.

I have distilled the problem down to the following situation. I want to write a function that will take as input an array consisting of arrays of arrays, e.g. A = [[[1]]]. I don't know the standard terminology for this, so I am going to refer to the main array as "level 0", which has elements that are arrays of "level 1". I'll say the level 1 arrays consist of arrays of "level 2". The level 2 arrays consist of integers.

The function does the following, on input A (a level 0 array):

  1. create an empty array L;
  2. for each level 1 array M in A
    • add one to each integer entry in each level 2 array in M;
    • add two copies of M to L
  3. return L.

Here is my code:

function myFunc(A){
  var L = [];
  for(var a=0; a<A.length; a++){
    var M = A[a].slice(0);
    for(var i=0; i<M.length; i++){
      for(var j=0; j<M[i].length; j++){
        M[i][j]++;
      }
    }
    for(var s=0; s<2; s++){
      var N = M.slice(0);
      L.push(N);
    }
  }
  return(L);
}

Now I test it out:

var A = [[[1]]];

A = myFunc(A)

After this, I get A = [[[2]],[[2]]], which is what I expect. However, suppose I iterate it:

var A = [[[1]]];

A = myFunc(A);

A = myFunc(A);

Then I expect to obtain A = [[[3]],[[3]],[[3]],[[3]]], but instead I have A = [[[4]],[[4]],[[4]],[[4]]].

On the other hand if I run myFunc([[[2]],[[2]]]), I do get the expected [[[3]],[[3]],[[3]],[[3]]].

I don't understand where this discrepancy is ing from.

Share Improve this question asked Apr 12, 2015 at 20:33 Gabe ConantGabe Conant 3771 gold badge5 silver badges14 bronze badges 8
  • 1 That's not the issue. The function when called twice does not have the same results, read his question again. I'm investigating and I got a lead I think – Tristan Foureur Commented Apr 12, 2015 at 20:50
  • 1 Its probably a referenced value vs a copied one. Try A = JSON.parse(JSON.stringify(A)) between your A = myFunct(A) calls. I cannot test I am on my iPhone – Flavien Volken Commented Apr 12, 2015 at 20:50
  • 1 @FlavienVolken yes that does solve it but I do not understand why it's failing here. :o – Tristan Foureur Commented Apr 12, 2015 at 20:53
  • 1 Still digging, weird behavior found, do x = [[[1]]] and then call myFunc(x) multiple times. – Tristan Foureur Commented Apr 12, 2015 at 21:01
  • I think the issue has to do with var M = A[a].slice(0). Since it's a multidimensional array, it gets a reference to the original A array. Then when you increment M[i][j]++ it's altering A. Same as what @TristanFoureur found in his answer :) – concrete_d Commented Apr 12, 2015 at 21:11
 |  Show 3 more ments

3 Answers 3

Reset to default 5

The problem is the line:

M[i][j]++;

Node keeps this as a reference to your A's slice, and you see it clearly when you do this :

x = [[[1]]];
myFunc(x);
myFunc(x);
console.log(x); // ---> [[[3]]]

For a shallow copy you'd have to use the JSON.parse(JSON.stringify()) trick, and proof that M is the problem; adding this line just after M = A[a].slice(0); solves the issue.

M = JSON.parse(JSON.stringify(M))

Mozilla's documentation about Array.prototype.slice() :

For object references (and not the actual object), slice copies object references into the new array. Both the original and new array refer to the same object. If a referenced object changes, the changes are visible to both the new and original arrays.

Source

That is why, because when you do M[i][j], the array a level deeper is still referenced outside.

Since Tristan Foureur has already pointed out what caused the discrepancy, I just wanted to add in why

var A = [[[1]]];

A = myFunc(A);
A = myFunc(A);

gives a different result versus

myFunc([[[2]],[[2]]])

When doing myFunc([[[2]],[[2]]]), what you are basically doing is myFunc(new Array(new Array(new Array(2))),(new Array(new Array(2))))).

So when the V8 engine elevates this line

var M = A[a].slice(0);

it would interpret A[a].slice(0) as

new Array(new Array(new Array(2))),(new Array(new Array(2))))[a].slice(0)

and hence you get 2 (from the new Array) every time it is called.

This can be seen if you examine the function with logs:

function myFunc(A){
  var L = [];
  console.log("A is"+ JSON.stringify(A));
  for(var a=0; a<A.length; a++){
    var M = A[a].slice(0);
    console.log("M is"+ JSON.stringify(M) + " while A is" +JSON.stringify(A),a); 
    for(var i=0; i<M.length; i++){
      for(var j=0; j<M[i].length; j++){
        M[i][j]++;
      }
    }
    for(var s=0; s<2; s++){
      var N = M.slice(0);
      L.push(N);
      console.log("L.push:"+ JSON.stringify(N));
    }
  }
  console.log("the end" + JSON.stringify(L), JSON.stringify(A));
  return(L);
}

var A = [[[1]]];
A = myFunc(A);
A = myFunc(A);
var t = myFunc([[[2]],[[2]]]);

If you replace var M = A[a].slice(0); with

var M = new Array(new Array(new Array(2))),(new Array(new Array(2))))[a].slice(0)

and run through the function without A, you would see [[[3]],[[3]],[[3]],[[3]]].

As others have already mentioned the issue is that the M.slice call only did a shallow copy. They have also given good solutions for how to do a deepcopy. I'd propose that you really don't need to do the copy at all though:

var L = [];
for (var iLevel1 = 0; iLevel1 < A.length; iLevel1++)
{
    for (var iLevel2 = 0; iLevel2 < A[iLevel1].length; iLevel2++)
    {
        for (var iLevel3 = 0; iLevel3 < a[iLevel1][iLevel2].length; iLevel3++)
           L.push(a[iLevel1][iLevel2][iLevel3] + 1); 
    }
}

return L.concat(L);
发布评论

评论列表(0)

  1. 暂无评论