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.