Abbadiah
Abbadiah

Reputation: 171

Javascript .map() on copied array

I have noticed that invoking .map() without assigning it to a variable makes it return the whole array instead of only the changed properties:

const employees = [{
    name: "John Doe",
    age: 41,
    occupation: "NYPD",
    killCount: 32,
  },
  {
    name: "Sarah Smith",
    age: 26,
    occupation: "LAPD",
    killCount: 12,
  },
  {
    name: "Robert Downey Jr.",
    age: 48,
    occupation: "Iron Man",
    killCount: 653,
  },

]

const workers = employees.concat();

workers.map(employee =>
  employee.occupation == "Iron Man" ? employee.occupation = "Philantropist" : employee.occupation
);

console.log(employees);

But considering that .concat() created a copy of the original array and assigned it into workers, why does employees get mutated as well?

Upvotes: 2

Views: 12151

Answers (4)

try-catch-finally
try-catch-finally

Reputation: 7624

Problem analysis

workers = workers.map(employee => 
  employee.occupation == "Iron Man" ? (employee.occupation = "Philantropist", employee) : (employee.occupation, employee)
);

[...] why does employees get mutated as well?

array.map() calls the passed function with each element from the array and returns a new array containing values returned by that function.

Your function just returns the result of the expression

element.baz = condition ? foo : bar;

which, depending on the condition, will

  • evaluate to foo or bar
  • assign that result to baz and
  • return that result

Further (expression1, expression2) will evaluate both expressions and return expression2 (see the comma operator).

So, although you return employee in both cases, you modify the original object with the left expression (expression 1).

Possible solution

You might want to create a new object using Object.assign()

array.map((employee) => Object.assign({ }, employee))

instead of using that array.concat() "trick". Using that mapping, you not only create a new array but also new objects with their attributes copied. Though this would not "deep copy" nested objects like { foo: { ... } } - the object accessible via the property foo would still be the same reference as the original. In such cases you might want to take a look on deep copying modules mentioned in the other answers.

Upvotes: 3

Nick Parsons
Nick Parsons

Reputation: 50674

This is happening because your objects within the array are still being referenced by same pointers. (your array is still referring to the same objects in memory). Also, Array.prototype.map() always returns an array and it's result should be assigned to a variable as it doesn't do in-place mapping. As you are changing the object's properties within your map method, you should consider using .forEach() instead, to modify the properties of the object within the copied employees array. To make a copy of your employees array you can use the following:

const workers = JSON.parse(JSON.stringify(employees));

See example below:

const employees = [
  {
    name: "John Doe",
    age: 41,
    occupation: "NYPD",
    killCount: 32,
  },
  {
    name: "Sarah Smith",
    age: 26,
    occupation: "LAPD",
    killCount: 12,
  },
  {
    name: "Robert Downey Jr.",
    age: 48,
    occupation: "Iron Man",
    killCount: 653,
  },

]


const workers = JSON.parse(JSON.stringify(employees));
workers.forEach(emp => {
  if(emp.occupation == "Iron Man") emp.occupation = "Philantropist";
});

console.log("--Employees--")
console.log(employees);
console.log("\n--Workers--");
console.log(workers);

  • Note: If your data has any methods within it you need to use another method to deep copy

Upvotes: 6

Jonas Wilms
Jonas Wilms

Reputation: 138247

map builds up a new array from the values returned from the callback, which caj easily be used to clone the objects in the array:

 const workers = employees.map(employee => ({
     ...employee, // take everything from employee
     occupation: employee.ocupation == "Iron Man" ? "Philantropist" : employee.occupation
 }));

Or you could deep clone the array, then mutate it with a simple for:

 const workers = JSON.parse(JSON.stringify(workers));
 for(const worker of workers)
   if(worker.ocupation == "Iron Man")
    worker.ocupation = "Philantropist";

Upvotes: 0

aaruja
aaruja

Reputation: 371

The array references change but the copied array still reference the original objects in the original array. So any change in the objects in the array are reflected across all copies of the array. Unless you do a deep copy of the array, there is a chance that the some changes in the inner objects get reflected across each copy

What is the difference between a deep copy and a shallow copy?

Deep copies can be made in several ways. This post discusses specifically that: What is the most efficient way to deep clone an object in JavaScript?

Upvotes: 0

Related Questions