Warm tip: This article is reproduced from stackoverflow.com, please click
ecmascript-6 functional-programming javascript

What's the best way of creating a new object with updated nested properties in modern JavaScript

发布于 2020-03-27 15:40:09

I have a JavaScript object with some nested properties that I want to update based on some conditions. The starting object could be something like:

const options = {
    formatOption: {
        label: 'Model Format',
        selections: {
            name: 'Specific Format',
            value: '12x28',
        }
    },
    heightOption: {
        label: 'Model Height',
        selections: {
            name: 'Specific Height',
            value: '15',
        }
    }
};

I have come up with a solution using Object.keys, reduce and the spread operator, but I would like to know if this is the best / more concise way as of today or if there is a better way. I'm not looking for the most performing option, but for a "best practice" (if there is one) or a more elegant way.

EDIT 30/01/20
As pointed out in the comments by @CertainPerformance my code was mutating the original options variable, so I am changing the line const option = options[key]; to const option = { ...options[key] };. I hope this is correct and that the function is not mutating the original data.

const newObject = Object.keys(options).reduce((obj, key) => {
  const option = { ...options[key] };
  const newVal = getNewValue(option.label); // example function to get new values
    // update based on existence of new value and key
    if (option.selections && option.selections.value && newVal) {
      option.selections.value = newVal;
    }
    return {
      ...obj,
      [key]: option,
    };
}, {});

getNewValue is an invented name for a function that I am calling in order to get an 'updated' version of the value I am looking at. In order to reproduce my situation you could just replace the line const newVal = getNewValue(option.label); with const newVal = "bla bla";

Questioner
Giorgio Tempesta
Viewed
47
Giorgio Tempesta 2020-02-07 01:28

A more concise version that has been suggested to me would be to use forEach() instead of reduce(). In this case the only difficult part would be to clone the original object. One way would be to use lodash's _.cloneDeep(), but there are plenty of options (see here).

Here is the code:

const newObject = _.cloneDeep(options);
Object.keys(newObject).forEach(key => {
    const newVal = getNewValue(newObject[key].label); // example function to get new values
    // update based on existence of new value and key
    if (newObject[key].selections && newObject[key].selections.value && newVal) {
        newObject[key].selections.value = newVal;
    }
});

The only problem is that forEach() changes values that are declared outside of the function, but reduce() can mutate its parameter (as it happened in my original solution), so the problem is not solved by using reduce() alone.

I'm not sure that this is the best solution, but it surely is much more readable for the average developer than my first try or the other solutions.