JS: how to use Object.assign to duplicate properties into an array of objects?

I'm trying to duplicate key-value pairs from one object into each distinct object inside an array.

const propsToDuplicate = {
  foo: 'foo',
  bar: 'bar'
};

const items = [{
    num: 1
  },
  {
    num: 2
  }
];

const result = items.map(item => {
  console.log('item: ', item);
  const moar = Object.assign(propsToDuplicate, item);
  console.log('item with more stuff: ', moar);
  return moar;
});

console.log(result);

Questions:

  1. Why do I end up with two instances of the object with num = 2?
  2. How can I perform this operation so that the final result is as below?

Desired result:

[ { foo: 'foo', bar: 'bar', num: 1 },
  { foo: 'foo', bar: 'bar', num: 2 } ]

Here is the sandbox:

https://repl.it/@montrealist/array-map-and-object-assign-weirdness

2 answers

  • answered 2018-01-11 21:00 casieber

    Object.assign(a, b, c) will assign everything from a, b, and c into the object that is the first parameter to assign().

    In your case you are using propsToDuplicate as the first parameter, and this means that that each time assign() is called, propsToDuplicate is being mutated.

    Change const moar = Object.assign(propsToDuplicate, item); to const moar = Object.assign({}, propsToDuplicate, item); and you should be good to go.

  • answered 2018-01-11 21:28 Thomas

    Why do I end up with two instances of the object with num = 2

    because moar === propsToDuplicate in your code. You've assigned all these properties/values to the very same object. And all indices of that array reference the same object.

    How can I perform this operation so that the final result is as below?

    assign the properties to an empty object:

    const morar = Object.assign({}, propsToDuplicate, item);
    

    The workaround is to return JSON.parse(JSON.stringify(moar));

    this doesn't really work, as you still assign all the properties into the same object, but you return a snapshot of each iteration. If you add another property only to the first object in items you'll see it show up for the other(later) items as well.

    Plus JSON.parse(JSON.stringify(...)) is a ugly approach to clone an object.

    Or I could simply flip the parameters - would this work? Object.assign(item, propsToDuplicate);

    yes/no/kind of/better not ... I have to explain.

    In that case, you'll overwrite the properties in item with the properties in propsToDuplicate, and you'd mutate the objects in the items array. In your current code this would make no difference, but if some item would have a foo or bar property or if propsToDuplicate would contain a num, you'd overwrite that property in the result.

    explaining that: Object.assign() is often used to compose some config object with some default values.

    let options = Object.assign({}, defaults, config);
    

    this will take an empty Object, then first write all the default values into that and then overwrite soem of the default values with the values passed in the config.

    whereas

    let options = Object.assign(config, defaults);
    

    will overwrite all the custom configutations with the default values.

    Then there's the problem of mutation. The problem with mutation is that depending where you got the object from, and where else it is referenced, you changing the object may introduce errors at the other end of your application, in some completely unrelated peice of code. And then have fun debugging that and finding the error.