Search code examples
javascriptnode.jsdictionaryfunctor

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


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.


Solution

  • You're not modifying your original array. You're modifying the objects in the array. If you want to avoid mutating the objects in your array, you can use Object.assign to create a new object with the original's properties plus any changes you need:

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

    2018 Edit:

    In most browsers you can now use the object spread syntax instead of Object.assign to accomplish this:

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