3

I'm trying to assign the value of an array element to an object. After first attempting something like, e.g.bar = foo[0]; I've discovered that any change to bar also changes foo[0], due to having the same reference.

Awesome, thought no one, and upon reading up on immutability and the ES6 Object.assign() method and spread properties, I thought it would fix the issue. However, in this case it doesn't. What am I missing?

EDIT: Sorry about the accountTypes confusion, I fixed the example. Also, I would like to keep the class structure of Settings, so let copy = JSON.parse(JSON.stringify(original)); is not really what I'm after in this case.

//this object will change according to a selection
currentPreset;

//this should remain unchanged
presets: {name: string, settings: Settings}[] = [];


  ngOnInit()
  {
    this.currentPreset = {
      name: '',
      settings: new Settings()
    }

    this.presets.push({name: 'Preset1', settings: new Settings({
        settingOne: 'foo',
        settingTwo: false,
        settingThree: 14
      })
    });

  }

 /**
  * Select an item from the `presets` array and assign it, 
  * by value(not reference), to `currentPreset`.
  * 
  * @Usage In an HTML form, a <select> element's `change` event calls 
  * this method to fill the form's controls with the values of a
  * selected item from the `presets` array. Subsequent calls to this
  * method should not affect the value of the `presets` array.
  *
  * @param value - Expects a numerical index or the string 'new'
  */
  setPreset(value)
  {
    if(value == 'new')
    {
      this.currentPreset.name = '';
      this.currentPreset.settings.reset();
    }
    else
    {
      this.currentPreset = {...this.presets[value]};
      //same as above
      //this.currentPreset = Object.assign({}, this.presets[value]);
    }
  }
slanden
  • 1,199
  • 2
  • 15
  • 35
  • Can you provide a more complete or better example? What is `accountTypes`? What is `value`? What is value that you don't wanted to get mutated, what is is before the mutation and what is is after? – Felix Kling Apr 24 '18 at 21:12
  • 1
    With these operators you are doing a shadow copy of `Settings` which is the same instance in all the updates – botika Apr 24 '18 at 21:12
  • 5
    Using object spread or `Object.assign` will create a *shallow copy* only. So mutating properties of that copy will still change the same properties in the original. You may want to try creating a deep copy. The answers to [this question](https://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-deep-clone-an-object-in-javascript?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa) show a few ways of doing so. – CRice Apr 24 '18 at 21:14

4 Answers4

4

Try this : let copy = original.map(item => Object.assign({}, ...item)); This will create a new object without any reference to the old object original

In case if you want to do this for an array try the same with []

let copy = original.map(item => Object.assign([], ...item));
hakuna
  • 6,243
  • 10
  • 52
  • 77
1

You have to do a deep copy, this the easiest way:

let copy = JSON.parse(JSON.stringify(original));
Luis Gonzalez
  • 531
  • 5
  • 16
  • 4
    Careful because JSON only stores plain objects, not a particular type of object with a custom protototype. I can't honestly tell if that matters to the OP's usage since they don't really show enough code to know, but if there's a `Settings` object or any other type of custom object, then this type of deep copy will not preserve the custom object type/prototype. – jfriend00 Apr 24 '18 at 21:21
  • @Luis, thanks for your answer. I forgot to mention it but, I do want to preserve the `Settings` object as jfriend00 pointed out. – slanden Apr 25 '18 at 14:04
0

This doesn't really answer the question, but since the object I'm trying not to mutate doesn't have nested properties within, I call the assignment at the property level and the shallow copy here is fine.

setPreset(value)
  {
    if(value == 'new')
    {
      this.currentPreset.name = '';
      this.currentPreset.settings.reset();
    }
    else
    {
      this.currentPreset.name = this.presets[value].name;
      this.currentPreset.privileges = Object.assign(new Settings(), 
                                         this.presets[value].settings);
    }
  }

A better solution, since I'm creating a new Settings() anyway, might be to move this logic to a Settings class method and call it in the constructor

slanden
  • 1,199
  • 2
  • 15
  • 35
0

I had the same problem recently, and I could not figure out why some of my objects were changing their properties. I had to change my code to avoid mutation. Some of the answers here helped me understand afterwards, such as this great article : https://alistapart.com/article/why-mutation-can-be-scary/

I recommend it. The author gives a lot of examples and useful libraries that can outperform Object.assign() when it comes to embedded properties.

JrmDel
  • 390
  • 1
  • 4
  • 16