When you write :
xs = [
{ p : true },
{ p : false }
];
ys = xs.filter(
x => x.p
);
Here is how it looks like in memory :
xs { p : false }
\ /
[ 0 , 1 ]
\
{ p : true }
/
[ 0 ]
/
ys
As you said, .filter()
creates a new array, that's why xs
and ys
are linked to different arrays. Then, since xs[0].p
is true, it makes a copy of xs[0]
and pushes it to the new array. What you need to realize here is that xs[0]
is a link to { p : true }
, it's not the object itself, and ys[0]
is a copy of this link. As a consequence, xs[0]
and ys[0]
are linked to the same object, and if you write xs[0].p = false
, you can read the update with ys[0].p
.
xs[0]
\
{ p : false }
/
ys[0]
If you want to avoid this situation, you have to copy the object itself :
function copy (x) {
var y = {}; // new object
for (var k in x) y[k] = x[k];
return y;
}
ys[0] = copy(ys[0]);
Since copy()
returns a new object, xs[0]
and ys[0]
are now linked to different objects, thus, changes to one object won't affect the other :
xs[0].p = true;
xs[0] --- { p : true }
ys[0] --- { p : false }
Regarding your code, arr[0]
is a link to { a: "one", b: "two" }
, and .filter()
creates new arrays that contains a copy of this link pointing to the same object :
res.arr1[0]
\
\
arr[0] ----- { a: "one", b: "two" }
/
/
res.arr2[0]
Again, if you want to avoid this situation, you have to copy the object itself. However, since arr
is likely to contain more than one object, you have to iterate over the array to copy every object one after the other. .map()
is perfect for this job :
res.arr1 = arr.filter(f).map(copy);
res.arr2 = arr.filter(f).map(copy);
res.arr1[0] --- { a: "one", b: "two" }
arr[0] -------- { a: "one", b: "two" }
res.arr2[0] --- { a: "one", b: "two" }
Be careful though, it's a bit trickier when it comes to nested objects :
xs = [{ p: {} }]; // nested objects
ys = [copy(xs[0])];
In the above case, xs[0]
and ys[0]
are different, but xs[0].p
and ys[0].p
are the same :
xs[0]
\
{ p }
\
{}
/
{ p }
/
ys[0]
In such a case, you have to make a deep copy of the object. This function will do the trick :
function copyTree (x) {
if (x instanceof Array) {
var copy = [];
for (var i = 0; i < x.length; i++) {
copy[i] = copyTree(x[i]);
}
return copy;
} else if (x === Object(x) && typeof x !== "function") {
var copy = {};
for (var k in x) {
copy[k] = copyTree(x[k]);
}
return copy;
} else {
return x;
}
}
res.arr1 = arr.filter(f).map(copyTree);
res.arr2 = arr.filter(f).map(copyTree);
Note that the same problem arises with nested arrays, hence the array test above.