Javascript: Modifying nested object in ES6 shorthand

Viewed 748

Consider a function returns an nested object and I want to modify the property inside the nested object.

In the below example, I'm calling the function many times or I need to store it in a temporary variable. Is there a way to invoke only once inside the braces and spread/modify inside the same object many times.

const getObject = () => {
   return {
     a: {
      b: {
        c: 1,
        d: 2,
      }
     },
     e: 3
   }
}

var modifiedD = {
  ...getObject(),
  a: {
     b: {
      ...getObject().a.b,
      d: 4
     }
  }
}

console.log(modifiedD);

3 Answers

when declaring a key after ...getObject() it replace the whole value. It does not merge the inner object behind a.

So you could do it as you have done and call getObject() multiple time.


An other solution could be to handle it using a function of your own merging the objects, like :

function mergeObjects(obj1, obj2) {
  // We are going to copy the value of each obj2 key into obj1
  Object.keys(obj2).forEach((x) => {
    // If we have an object, we go deeper
    if (typeof obj2[x] === 'object') {
      if (obj1[x] === void 0) {
        obj1[x] = {};
      }

      mergeObjects(obj1[x], obj2[x]);
    } else {
      obj1[x] = obj2[x];
    }
  });
  
  return obj1;
}

const getObject = () => {
  return {
    a: {
      b: {
        c: 1,
        d: 2,
      }
    },
    e: 3
  }
}

const modifiedD = mergeObjects(getObject(), {
  a: {
    b: {
      d: 4,
    },
  },
});

console.log(modifiedD);

WARNING, the function I have made mutate the object which may not be the best answer


Or call it only once and then set the keys one by one like :

const getObject = () => {
   return {
     a: {
      b: {
        c: 1,
        d: 2,
      }
     },
     e: 3
   }
}

const modifiedD = getObject();

modifiedD.a.b.d = 4;

console.log(modifiedD);

Further to my previous answer, as Grégory NEUT pointed out you could have a lot larger complexity.

If so, you could simply create two objects and then merge them. I found a function code snippet to be able to do that using Object.assign

Example:

const getObject = () => {
   return {
     a: {
      b: {
        c: 1,
        d: 2,
      }
     },
     e: 3
   }
}

var modifiedD = getObject();
var newD = {
  a: {
     b: {
       d: 4
     },
     y: 1
  },
  z: 20
}

/** TAKEN FROM https://gist.github.com/ahtcx/0cd94e62691f539160b32ecda18af3d6 **/
// Merge a `source` object to a `target` recursively
const merge = (target, source) => {
  // Iterate through `source` properties and if an `Object` set property to merge of `target` and `source` properties
  for (let key of Object.keys(source)) {
    if (source[key] instanceof Object) Object.assign(source[key], merge(target[key], source[key]))
  }

  // Join `target` and modified `source`
  Object.assign(target || {}, source)
  return target
}

modifiedD = merge(modifiedD, newD);

console.log(modifiedD);

You can try the following:

getParentObj(path, obj) {
   return path.split('.').reduce((o,i)=>o[i], obj);
}

const parent = getParentObj('a.b', getObject());
parent[d] = 24;
Related