6

Why does map mutate array of objects?

var obj = {
  items: [{
    value: 1,
    selected: true
  }, {
    value: 2,
    selected: false
  }]
};

var items = obj.items.map(i => {
  if (i.value === 2) i.selected = true;
  return i;
});

console.log(obj);
vvvvv
  • 25,404
  • 19
  • 49
  • 81
terreb
  • 1,417
  • 2
  • 23
  • 40

4 Answers4

7

If you want a quick solution for an unmutable version of .map over an array of objects you can use the spread operator:

myArrayOfObjects.map(({...obj}) => { });

Example:

const foo = [];

for(let i = 0; i < 5; i++) {
    foo.push({label: "foo"});
}

const bar = foo.map(({...val}) => {
    val.id = Math.random();
  return val;
});

console.log(foo);
console.log(bar);
Manu Schiller
  • 3,256
  • 2
  • 11
  • 13
5

When you map an array, it's not creating a copy of the object. It's just iterating over the array.

If you don't want to mutate the object, you have to create a copy of the object:

var items = obj.items.map(item => {
    let i = JSON.parse(JSON.stringify(item))
    if (i.value === 2) i.selected = true;
    return i;
});
Hammerbot
  • 15,696
  • 9
  • 61
  • 103
1

.map() as Hammerbot explained, does not create a copy, it creates a new array which directly references your object, thus mutating your object.

If you don't want to mutate your object, you can use Object.assign() within your mapping which creates a copy of the object.

Alex
  • 2,164
  • 1
  • 9
  • 27
  • 2
    And what if the object you are copying using ‘Object.assign’ has objects in it? Those child objects are still a reference to those in memory in the original array. ‘Object.assign’ only does a shallow copy. – Modermo Feb 06 '19 at 07:47
0

First for reference...

.map is an Array method and not an Object method. When .map is appropriately utilized on an Array, it will return a new array with each element being the result of the callback function. Check out the first example in MDN Web Doc for Array.prototype.map() There it states, in the description, "the map() method is a copying method" which basically means it will not mutate the original array. Instead it will essentially iterate over each item from the array, perform some expression (through the callback function), then return the result value as a single element into a new array. This isn't exact but paints the picture. I recommend reading Copying Methods and Mutating Methods and shallow copy to better understand how exactly the "copying" works. After iterating through all the elements of an array the new array will be returned containing the new element values.

The Juicy Part... Now, attempting to utilize the map() method on an object does not create a copy of the object. Instead you would be directly referencing the object and directly mutating its properties. When working with Objects in such a way you can utilize it's native methods like Object.assign, Object.keys, Object.values, or Object.entries to create an array to then utilize .map() on. Here's a nice article with some examples. Another helpful item is the for...in statement. You can utilize this to iterate over an objects properties then execute some code to either perform a check or modify the property value. Below is an example.

let names = ["Superman", "Batman"];

let players = names.map((name) => ({
  Name: name,
  Score: 0,
  IsActivePlayer: false
}));

players.forEach((player) => {
  for (const prop in player) {
    if ((prop === "Name") && (player[prop] === "Superman")) {
      console.log("I do bleed");
      break;
    }
  }
});

Some last items I would suggest is reading on the subject of the spread syntax for object literals. Here are some supplemental reads. Object spread vs Object.assign understanding spread syntax

Miguel
  • 1
  • 1