0

I'm trying to update an deeply nested object without overriding existing properties, but can't find an elegant way to do this, for example:

const data = {
  items: {
    one: {
      active: false,
      id: '123'
    },
    two: {
      active: true
    }
  }
}

const updatedData = {
  items: {
     one: {
        active: true
     }
  }
}

The end result should be:

{
  items: {
    one: {
      active: true,
      id: '123'
    },
    two: {
      active: true
    }
  }
}

However, using Object.assign or spread operator, will replace items.one with only {active: true} and not retain the id. Is there a way to do this without recursively going through the object?

dzm
  • 22,844
  • 47
  • 146
  • 226
  • 3
    related: https://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically – Jeff Mar 29 '18 at 16:25
  • If you use lodash in your project, it has a `_.merge` method to do exactly this. – CRice Mar 29 '18 at 16:43
  • @CRice Yeah, that's probably cleanest, I ended up using http://ramdajs.com/docs/#mergeDeepLeft – dzm Mar 29 '18 at 18:42

3 Answers3

0
   function merge(source, into) {
     for(let key in into){
       if(typeof into[key] === "object") {
         merge(source[key] || (source[key] = {}), into[key]);
       } else {
         source[key] = into[key];
      }
   }
 }
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
0

A recursive function makes it pretty simple. Iterate the properties of the new data. If both the target and source for a property reference an object, call the function recursively with the two objects. Otherwise just assign the source value to the target value.

const data = {
  items: {
    one: {
      active: false,
      id: '123'
    },
    two: {
      active: true
    }
  }
}

const updatedData = {
  items: {
     one: {
        active: true
     }
  }
}

updateWith(data, updatedData);
console.log(data);

function updateWith(target, newData) {
  for (const [k, v] of Object.entries(newData)) {
    if (typeof v === "object" && typeof target[k] === "object") {
      updateWith(target[k], v);
    } else {
      target[k] = v;
    }
  }
}
0

Given that you reference an arbitrary data.items[key] object, you can do the following:

data.items[key] = Object.assign({}, data.items[key], updatedData.items[key]);

The above will replace the old object value with a new one, with properties copied from the original and ultimately replacing any properties provided by the updated data.

nbkhope
  • 7,360
  • 4
  • 40
  • 58