Me gustaría agregar algunos datos a un objeto de JavaScript anidado, sin embargo, no sé cuánto del árbol de objetos ya existe. Quiero agregar la estructura si no está allí, pero también mantener la misma información si está etiquetada con otros datos predeterminados.
Idealmente, no quiero usar una biblioteca de terceros para esto.
En mi ejemplo, tengo la siguiente estructura de objeto, habrá una combinación infinita de estos disponibles, por lo que necesita generar la estructura para mí si aún no existe.
{ level1: { level2: { level3: { property1: 'test' } } } }Quiero poder darle a esto algunos valores, crear la estructura; si la estructura está allí, agregue algunos valores predeterminados y, si no, cree la estructura con los valores predeterminados.
Tengo lo siguiente, que hace el trabajo, pero me parece muy complicado, me preguntaba si había una mejor manera de lograr esto.
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 }Podría hacer algo como esto, donde pasa las teclas y recorre el objeto nivel por nivel. Cada ciclo establecemos una variable externa - level - al actual, por lo que no tenemos que usar las teclas anteriores para llegar al nivel actual. es decir, evitamos tener que hacer obj[ level1 ][ level2 ][ level3 ] = ... , en su lugar, podemos simplemente hacer level = ...
De esta manera, diría que es mejor pasar keys de un solo parámetro, que es una matriz. Con esto, no está limitado a exactamente 3 claves anidadas, sino que puede pasar cualquier número que desee.
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 );Tenga en cuenta que esto cambia el objeto en su lugar.
Esto podría hacerse a través de la recursividad. A continuación se muestra un esquema de cómo implementarlo (no probado para todos los casos):
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);Una variante menor de una función setPath que uso a menudo haría esto de una manera bastante sencilla:
const mixDeep = ([p, ...ps]) => (v) => (o) => p == undefined ? v : Object .assign ( Array .isArray (o) || Number .isInteger (p) ? [] : {}, {...o, [p]: ps .length ? mixDeep (ps) (v) ((o || {}) [p]) : {...v, ...(o || {}) [p]}}, ) const myFunc = mixDeep (['level1', 'level2', 'level3']) ({some: 'default', props: 'here'}) console .log (myFunc ({foo: 'bar'})) console .log (myFunc ({foo: 'bar', level1: {baz: 'qux'}})) console .log (myFunc ({foo: 'bar', level1: {baz: 'qux', level2: {corge: 'grault'}}})) console .log (myFunc ({foo: 'bar', level1: {baz: 'qux', level2: {corge: 'grault', level3: {garply: 'waldo'}}}})) .as-console-wrapper {max-height: 100% !important; top: 0} setPath establece el valor en la ruta, creando nodos intermedios según sea necesario. mixDeep mezcla el valor con el valor al final de la ruta, nuevamente construyendo los nodos intermedios que faltan.