recorrer objetos objeto lista eliminar elemento construir clases buscar array javascript ecmascript-5

javascript - objetos - Establecer dinĂ¡micamente la propiedad del objeto anidado



recorrer array de objetos javascript (12)

Tengo un objeto que podría tener varios niveles de profundidad y podría tener cualquier propiedad existente. Por ejemplo:

var obj = { db: { mongodb: { host: ''localhost'' } } };

En eso me gustaría establecer (o sobrescribir) las propiedades de esta manera:

set(''db.mongodb.user'', ''root''); // or: set(''foo.bar'', ''baz'');

Donde la cadena de propiedad puede tener cualquier profundidad, y el valor puede ser cualquier tipo / cosa.
Los objetos y matrices como valores no necesitan combinarse, si la clave de propiedad ya existe.

El ejemplo anterior produciría el siguiente objeto:

var obj = { db: { mongodb: { host: ''localhost'', user: ''root'' } }, foo: { bar: baz } };

¿Cómo puedo realizar tal función?


ES6 también tiene una forma genial de hacer esto usando Nombre de propiedad computada y Parámetro de reposo .

const obj = { levelOne: { levelTwo: { levelThree: "Set this one!" } } } const updatedObj = { ...obj, levelOne: { ...obj.levelOne, levelTwo: { ...obj.levelOne.levelTwo, levelThree: "I am now updated!" } } }

Si levelThree es una propiedad dinámica, es decir, para establecer cualquiera de las propiedades en levelTwo , puede usar [propertyName]: "I am now updated!" donde propertyName contiene el nombre de la propiedad en levelTwo .


Esta función, usando los argumentos que especificó, debe agregar / actualizar los datos en el contenedor obj . Tenga en cuenta que debe hacer un seguimiento de qué elementos del esquema obj son contenedores y cuáles son valores (cadenas, entradas, etc.), de lo contrario, comenzará a lanzar excepciones.

obj = {}; // global object function set(path, value) { var schema = obj; // a moving reference to internal objects within obj var pList = path.split(''.''); var len = pList.length; for(var i = 0; i < len-1; i++) { var elem = pList[i]; if( !schema[elem] ) schema[elem] = {} schema = schema[elem]; } schema[pList[len-1]] = value; } set(''mongo.db.user'', ''root'');


Inspirado por la respuesta de @ bpmason1:

function leaf(obj, path, value) { const pList = path.split(''.''); const key = pList.pop(); const pointer = pList.reduce((accumulator, currentValue) => { if (accumulator[currentValue] === undefined) accumulator[currentValue] = {}; return accumulator[currentValue]; }, obj); pointer[key] = value; return obj; }

Ejemplo:

const obj = { boats: { m1: ''lady blue'' } }; leaf(obj, ''boats.m1'', ''lady blue II''); leaf(obj, ''boats.m2'', ''lady bird''); console.log(obj); // { boats: { m1: ''lady blue II'', m2: ''lady bird'' } }


Lodash tiene un método _.set() .

_.set(obj, ''db.mongodb.user'', ''root''); _.set(obj, ''foo.bar'', ''baz'');


Lodash tiene un método llamado update que hace exactamente lo que necesita.

Este método recibe los siguientes parámetros:

  1. El objeto para actualizar
  2. La ruta de la propiedad para actualizar (la propiedad puede estar profundamente anidada)
  3. Una función que devuelve el valor para actualizar (dado el valor original como parámetro)

En tu ejemplo, se vería así:

_.update(obj, ''db.mongodb.user'', function(originalValue) { return ''root'' })


Otro enfoque es usar la recursión para cavar a través del objeto:

(function(root){ function NestedSetterAndGetter(){ function setValueByArray(obj, parts, value){ if(!parts){ throw ''No parts array passed in''; } if(parts.length === 0){ throw ''parts should never have a length of 0''; } if(parts.length === 1){ obj[parts[0]] = value; } else { var next = parts.shift(); if(!obj[next]){ obj[next] = {}; } setValueByArray(obj[next], parts, value); } } function getValueByArray(obj, parts, value){ if(!parts) { return null; } if(parts.length === 1){ return obj[parts[0]]; } else { var next = parts.shift(); if(!obj[next]){ return null; } return getValueByArray(obj[next], parts, value); } } this.set = function(obj, path, value) { setValueByArray(obj, path.split(''.''), value); }; this.get = function(obj, path){ return getValueByArray(obj, path.split(''.'')); }; } root.NestedSetterAndGetter = NestedSetterAndGetter; })(this); var setter = new this.NestedSetterAndGetter(); var o = {}; setter.set(o, ''a.b.c'', ''apple''); console.log(o); //=> { a: { b: { c: ''apple''}}} var z = { a: { b: { c: { d: ''test'' } } } }; setter.set(z, ''a.b.c'', {dd: ''zzz''}); console.log(JSON.stringify(z)); //=> {"a":{"b":{"c":{"dd":"zzz"}}}} console.log(JSON.stringify(setter.get(z, ''a.b.c''))); //=> {"dd":"zzz"} console.log(JSON.stringify(setter.get(z, ''a.b''))); //=> {"c":{"dd":"zzz"}}


Podemos usar una función de recursión:

/** * Sets a value of nested key string descriptor inside a Object. * It changes the passed object. * Ex: * let obj = {a: {b:{c:''initial''}}} * setNestedKey(obj, [''a'', ''b'', ''c''], ''changed-value'') * assert(obj === {a: {b:{c:''changed-value''}}}) * * @param {[Object]} obj Object to set the nested key * @param {[Array]} path An array to describe the path(Ex: [''a'', ''b'', ''c'']) * @param {[Object]} value Any value */ export const setNestedKey = (obj, path, value) => { if (path.length === 1) { obj[path] = value return } return setNestedKey(obj[path[0]], path.slice(1, path.length), value) }

¡Es más simple!


Si desea una función que requiera que existan propiedades anteriores, entonces podría usar algo como esto, también devolvería un indicador que indique si logró encontrar y establecer la propiedad anidada.

function set(obj, path, value) { var parts = (path || '''').split(''.''); // using ''every'' so we can return a flag stating whether we managed to set the value. return parts.every((p, i) => { if (!obj) return false; // cancel early as we havent found a nested prop. if (i === parts.length - 1){ // we''re at the final part of the path. obj[parts[i]] = value; }else{ obj = obj[parts[i]]; // overwrite the functions reference of the object with the nested one. } return true; }); }


Si solo necesita cambiar los objetos anidados más profundos, entonces otro método podría ser hacer referencia al objeto. Como los objetos JS son manejados por sus referencias, puede crear una referencia a un objeto al que tenga acceso de clave de cadena.

Ejemplo:

// The object we want to modify: var obj = { db: { mongodb: { host: ''localhost'', user: ''root'' } }, foo: { bar: baz } }; var key1 = ''mongodb''; var key2 = ''host''; var myRef = obj.db[key1]; //this creates a reference to obj.db[''mongodb''] myRef[key2] = ''my new string''; // The object now looks like: var obj = { db: { mongodb: { host: ''my new string'', user: ''root'' } }, foo: { bar: baz } };


Solo escribo una pequeña función usando recursión ES6 + para lograr el objetivo.

updateObjProp = (obj, value, propPath) => { const [head, ...rest] = propPath.split(''.''); !rest.length ? obj[head] = value : this.updateObjProp(obj[head], value, rest); } const user = {profile: {name: ''foo''}}; updateObjProp(user, ''fooChanged'', ''profile.name'');

Lo usé mucho en reaccionar al estado de actualización, funcionó bastante bien para mí.


Un poco tarde, pero aquí hay una respuesta no más simple de la biblioteca:

/** * Dynamically sets a deeply nested value in an object. * Optionally "bores" a path to it if its undefined. * @function * @param {!object} obj - The object which contains the value you want to change/set. * @param {!array} path - The array representation of path to the value you want to change/set. * @param {!mixed} value - The value you want to set it to. * @param {boolean} setrecursively - If true, will set value of non-existing path as well. */ function setDeep(obj, path, value, setrecursively = false) { let level = 0; path.reduce((a, b)=>{ level++; if (setrecursively && typeof a[b] === "undefined" && level !== path.length){ a[b] = {}; return a[b]; } if (level === path.length){ a[b] = value; return value; } else { return a[b]; } }, obj); }

Esta función que hice puede hacer exactamente lo que necesita y un poco más.

digamos que queremos cambiar el valor objetivo que está profundamente anidado en este objeto:

let myObj = { level1: { level2: { target: 1 } } }

Así que llamaríamos a nuestra función así:

setDeep(myObj, ["level1", "level2", "target1"], 3);

resultará en:

myObj = {level1: {level2: {target: 3}}}

Establecer el indicador recursivamente conjunto en verdadero establecerá objetos si no existen.

setDeep(myObj, ["new", "path", "target"], 3);

dará como resultado esto:

obj = myObj = { new: { path: { target: 3 } }, level1: { level2: { target: 3 } } }


Creé gist para establecer y obtener valores obj por cadena en función de la respuesta correcta. Puede descargarlo o usarlo como paquete npm / hilo.

/** * Sets a value of nested key string descriptor inside a Object. * It changes the passed object. * Ex: * let obj = {a: {b:{c:''initial''}}} * setNestedKey(obj, [''a'', ''b'', ''c''], ''changed-value'') * assert(obj === {a: {b:{c:''changed-value''}}}) * * @param {[Object]} obj Object to set the nested key * @param {[Array]} path An array to describe the path(Ex: [''a'', ''b'', ''c'']) * @param {[Object]} value Any value */ export const setNestedKey = (obj, path, value) => { if (path.length === 1) { obj[path] = value return } return setNestedKey(obj[path[0]], path.slice(1, path.length), value) }