3

I want to create a copy of an array using the map function, but instead, I get the same reference. I don't understand why. I would like to have an alternative way to create a copy of an array.

I used the map function to create a copy of the original array and then used the filter function on the copy to mutate a single element. I would expect that the original array would not be mutated, but it actually is.

Can someone give me background info on why this is happening and also the right way to really copy an array, creating a reference to a different object?

const arr = [{name: 'hello'}, {name: 'world'}] 
const arr2 = arr.map(i => i)
const [partial] = arr2.filter(i => i.name === 'hello')
partial.name = 'HELLO'
arr2[0].name === 'HELLO' // true
arr[0].name === 'HELLO' // true
jo_va
  • 13,504
  • 3
  • 23
  • 47
AJavaStudent
  • 73
  • 2
  • 8
  • `map` creates a shallow copy, meaning the array itself and all primary values are copied. Objects are not primary values, meaning that this will result in a array copy with copies of the references. When pushing or popping items from the array you can see that it is indeed a copy. In the example you don't modify the array, but an object inside the array. https://stackoverflow.com/questions/55060656/copy-array-of-objects/55062555#55062555 – 3limin4t0r Apr 04 '19 at 12:06
  • 1
    @jo_va With the code example provided by OP it creates a shallow copy. `.map(i => i)` – 3limin4t0r Apr 04 '19 at 13:00
  • @3limin4t0r, in the OP's code you are totally right! – jo_va Apr 04 '19 at 13:05
  • @jo_va Your comment was well placed though since normally `map` is used to *map* items in an array to a new value. In this scenario OP might as well use `arr.slice()`. – 3limin4t0r Apr 04 '19 at 13:20

1 Answers1

3

Object and Arrays are reference types in JavaScript, which means, when you copy them, you are copying their reference, which still points to the original object.

So when you map with:

const arr2 = arr.map(i => i);

you are copying the items from arr to arr2 one by one. And since these items are objects, only their references is copied. You do have two distinct arrays, but they contain references to the same objects.

You could instead do a shallow copy of the inner objects using the spread operator when mapping:

const arr = [{name: 'hello'}, {name: 'world'}] 

const arr2 = arr.map(o => ({ ...o })) // spread operator + implicit return notation

arr[0].name = 'bye';
console.log(arr[0].name, arr2[0].name);

As mentionned by @3limin4t0r in the comments, you can also use Object.assign({}, o) instead of { ...o } to do a shallow copy, since the spread operator for properties is currently in ECMAScript proposal stage 4:

const arr = [{name: 'hello'}, {name: 'world'}] 

const arr2 = arr.map(o => Object.assign({}, o))

arr[0].name = 'bye';
console.log(arr[0].name, arr2[0].name);
jo_va
  • 13,504
  • 3
  • 23
  • 47
  • 1
    Alternatively you can use [`Object.assign({}, o)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) since the spread object operator is relatively new. – 3limin4t0r Apr 04 '19 at 13:24
  • 1
    The object/property spread operator is currently in proposal stage 4. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals – 3limin4t0r Apr 04 '19 at 13:58
  • 1
    @jo_va ah yes.. I totally forgot about it. Thank you very much. I tried doing the same with just primitives and then I do get different values. I understand the code you've written and will use it further when dealing with objects within arrays. – AJavaStudent Apr 05 '19 at 15:25