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):
- create an empty array
L
; - for each level 1 array
M
inA
- add one to each integer entry in each level 2 array in
M
; - add two copies of
M
toL
- add one to each integer entry in each level 2 array in
- 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):
- create an empty array
L
; - for each level 1 array
M
inA
- add one to each integer entry in each level 2 array in
M
; - add two copies of
M
toL
- add one to each integer entry in each level 2 array in
- 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 callmyFunc(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
3 Answers
Reset to default 5The 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);
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745161375a4614405.html
评论列表(0条)