Why does a js map on an array modify the original array?

Viewed 75437

I'm quite confused by the behavior of map().

I have an array of objects like this:

const products = [{
    ...,
    'productType' = 'premium',
    ...
}, ...]

And I'm passing this array to a function that should return the same array but with all product made free:

[{
    ...,
    'productType' = 'free',
    ...
}, ...]

The function is:

const freeProduct = function(products){
    return products.map(x => x.productType = "free")
}

Which returns the following array:

["free", "free", ...]

So I rewrote my function to be:

const freeProduct = function(products){
    return products.map(x => {x.productType = "free"; return x})
}

Which returns the array as intended.

BUT ! And that's the moment where I loose my mind, in both cases my original products array is modified.

Documentation around map() says that it shouldn't ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map ).

I even tried to create a clone of my array turning my function into this:

const freeProduct = function(products){
    p = products.splice()
    return p.map(x => {x.productType = "free"; return x})
}

But I still get the same result (which starts to drive me crazy).

I would be very thankful to anyone who can explain me what I'm doing wrong!

Thank you.

5 Answers

Unfortunately, whether the spread operator nor the object assign operator does a deep copy.... You need to use a lodash like function to get areal copy not just a reference copy.

const util = require('util');
const print = (...val) => {
    console.log(util.inspect(val, false, null, false /* enable colors */));
};
const _ = require('lodash');

const obj1 =     {foo:{bar:[{foo:3}]}};
const obj2 =     {foo:{bar:[{foo:3}]}};

const array = [obj1, obj2];

const objAssignCopy = x => { return Object.assign({}, x, {})};
const spreadCopy = x => { return {...x}};
const _Copy = x => _.cloneDeep(x);

const map1 = array.map(objAssignCopy);
const map2 = array.map(spreadCopy);
const map3 = array.map(_Copy);

print('map1', map1);
print('map2', map2);
print('map3', map3);
obj2.foo.bar[0].foo = "foobar";
print('map1 after manipulation of obj2', map1); // value changed 
print('map2 after manipulation of obj2', map2); // value changed
print('map3 after manipulation of obj2', map3); // value hasn't changed!

Array Iterator Array.map() creates the new array with the same number of elements or does not change the original array. There might be the problem with referencing if there is object inside the array as it copies the same reference, so, when you are making any changes on the property of the object it will change the original value of the element which holds the same reference.

The solution would be to copy the object, well, array.Splice() and [...array](spread Operator) would not help in this case, you can use JavaScript Utility library like Loadash or just use below mention code:

const newList = JSON.parse(JSON.stringify(orinalArr))

Array Destructuring assignment can be used to clone the object.

const freeProduct = function(products){
    p = products.splice()
    return p.map(({...x}) => {x.productType = "free"; return x})
}

This method will not modify the original object.

Related