Home > Mobile >  Update a JavaScript object recursively using values from another object
Update a JavaScript object recursively using values from another object

Time:03-21

I would like to add some data to a nested JavaScript object, however I do not know how much of the object tree already exists. I want to add the structure if it is not there, but also keep the same information if it is there tagged with some other default data.

Ideally I don't want to use a third party library for this.

In my example I have the following object structure, there will be an endless combination of these available, so it needs to generate the structure for me if it does not already exist.

{
  level1: {
    level2: {
      level3: {
        property1: 'test'
      }
    }
  }
}

I want to be able give this some values, create the structure - if the structure is there, add some default values and if not create the structure with defaults.

I have the following, which does the job, but it looks very messy to me, I was wondering if there was a better way to achieve this?

const updateNestedObject = (obj, level1, level2, level3, defaultProps) => {
  if (obj && obj[level1] && obj[level1][level2] && obj[level1][level2][level3]) {
    obj[level1][level2][level3] = { ...defaultProps, ...obj[level1][level2][level3] }
  } else if (obj && obj[level1] && obj[level1][level2] && !obj[level1][level2][level3]) {
    obj[level1][level2][level3] = defaultProps
  } else if (obj && obj[level1] && !obj[level1][level2]) { 
    obj[level1][level2] = { [level3]: defaultProps }
  } else if (obj && !obj[level1]) { 
    obj[level1] = { [level2]: { [level3]: defaultProps } }
  } else {
    obj = { [level1]: { [level2]: { [level3]: defaultProps } } }
  }
  return obj
}

CodePudding user response:

This could be done via recursion. Below is an outline of how to implement it (not tested for all cases):

function mergeRecursive(targetObject, sourceObject) {
  Object.keys(sourceObject).forEach(function(key) {
    if (typeof sourceObject[key] === "object") {
      if (targetObject[key] === undefined) {
        targetObject[key] = {};
      }
      mergeRecursive(targetObject[key], sourceObject[key]);
    } else {
      targetObject[key] = sourceObject[key];
    }
  });
}
let foo = {
  "level1": {
    "bar": "baz"
  }
};
let bar = {
  level1: {
    level2: {
      level3: {
        property1: "test"
      }
    }
  }
};
mergeRecursive(foo, bar);
console.log(foo);

CodePudding user response:

You could do something like this, where you loop over the keys and traverse the object level by level. Each loop we set an outside variable - level - to the current one, so we don't have to use the previous keys to reach the current level. i.e., we avoid having to do obj[ level1 ][ level2 ][ level3 ] = ..., instead, we can just do level = ...

In this way, I'd say it's better to pass a single parameter keys, which is an array. With this, you aren't limited to exactly 3 nested keys, but can pass any number you want to.

const updateNestedObject = ( obj, keys, defaultProps ) => {
  let level = obj;
  for ( let i = 0; i <= keys.length-1; i   ) {
    let key = keys[ i ];

    if ( i === keys.length-1 ) {
      level[ key ] = { ...defaultProps };
      return;
    }

    if ( !level[ key ] ) {
      level[ key ] = {};
    }

    level = level[ key ];
  }
};

const object = {};
updateNestedObject( object, [ "foo", "bar", "baz" ], { property: "test" } )
console.log( object );

const object2 = { foo: { bar: {} } };
updateNestedObject( object2, [ "foo", "bar", "baz" ], { property: "test" } )
console.log( object2 );

const object3 = { foo: { bar: {} } };
updateNestedObject( object3, [ "foo", "bar" ], { property: "test" } )
console.log( object3 );

Note that this does change the object in-place.

  • Related