recorrer - Código de clave de objeto JavaScript codificado. Estableciendo dinámicamente un valor anidado
recorrer objeto json javascript (5)
Recursion es lo que necesitas:
function setData(key,val,obj) {
if (!obj) obj = data; //outside (non-recursive) call, use "data" as our base object
var ka = key.split(//./); //split the key by the dots
if (ka.length < 2) {
obj[ka[0]] = val; //only one part (no dots) in key, just set value
} else {
if (!obj[ka[0]]) obj[ka[0]] = {}; //create our "new" base obj if it doesn''t exist
obj = obj[ka.shift()]; //remove the new "base" obj from string array, and hold actual object for recursive call
setData(ka.join("."),val,obj); //join the remaining parts back up with dots, and recursively set data on our new "base" obj
}
}
setData("key1", "updated value1"); // data.key1 == "updated value1"
setData("key2.nested1", 99); // data.key2.nested1 == 99
Estoy trabajando en una pequeña biblioteca que me permite hacer una codificación de valor de clave básica con objetos. Digamos que tengo el siguiente objeto:
var data = { key1: "value1", key2: { nested1: 1, nested2: "wowza!" } };
Y tengo la siguiente función de JavaScript:
var setData = function(path, value) {
eval("data." + path + "= value;");
};
Y en uso:
setData("key1", "updated value1"); // data.key1 == "updated value1"
setData("key2.nested1", 99); // data.key2.nested1 == 99
Esto funciona, sin embargo, me gustaría lograr lo anterior sin usar eval
. ¿Es esto posible o es la mejor manera de hacerlo?
EDITAR:
NOTA: se puede suponer que el valor que está configurando existe, al menos para la profundidad de la ruta - 1. Me preocupa más establecer el valor de un objeto existente.
Sí, es fácil. obj.prop = val
es lo mismo que obj[''prop''] = val
para que pueda volver a escribir setData de esta manera:
var setData = function (path, value) {
data[path] = value;
};
Y eso debería hacerlo.
Sin embargo, no estoy seguro de cuánto éxito tendrá con el 3er nivel (si tiene sentido). Obj.prop.prop no puede ser referenciado por obj [''prop.prop''] y en su lugar tendrá que ser referenciado por obj [''prop''] [''prop''], por lo que setData tendría que ser reescrito para tener eso en cuenta. Aunque no estoy teniendo mucha suerte con eso.
Editar: he hecho uno que (para mí) lo establece anidado como quieras, pero no más allá de eso. Desafortunadamente, si anida más profundo que sus ejemplos, entonces no veo una razón real para abandonar eval
. No he hecho ningún punto de referencia, pero en algún momento los cálculos serán más costosos desde el punto de vista computacional que incluso el eval
(lo cual es bastante difícil de lograr).
Esto es lo que se me ocurrió, que los establecerá al menos en un ''nivel'' de profundidad; es decir, funcionará con key2.nested1
pero no key2.nested1.i_love_nesting
, si tiene sentido:
var setData = function (path, value) {
if (path.indexOf(''.'') != -1) {
path = path.split(''.'');
for (var i = 0, l = path.length; i < l; i++) {
if (typeof(data[path[i]]) === ''object'') {
continue;
} else {
data[path[i - 1]][path[i]] = value;
}
}
} else {
data[path] = value;
}
};
Espero que esto ayude. Sin embargo, es posible que no haya escrito esto de la manera más eficiente ...
Creo que este problema se vuelve más fácil y más natural de resolver si puedes ser flexible en cómo estás especificando tu camino. Si en lugar de hacer algo como key2.nested1
puedes hacer {key2:{nested1:{}}}
(notación de objeto), se vuelve bastante trivial:
function setData(subtree, path){
var nodeName = Object.keys(path);
if(typeof path[nodeName] == ''object'')
setData(subtree[nodeName], path[nodeName]);
else
subtree[nodeName] = path[nodeName];
}
var data = { key1: "value1", key2: { nested1: 1, nested2: "wowza!" } };
// For the initial call, ''subtree'' is the full data tree
setData(data, {key2:{nested1:99}});
Simplemente está recurriendo al segundo arg de la llamada setData
, hasta que encuentre el valor. Cada vez que recurse, ingrese al árbol de data
en el punto especificado.
Esta función se puede modificar fácilmente para que acepte la notación de puntos que especificó, pero para mí este es un enfoque más limpio y natural (además, las operaciones de cadena son lentas).
Aclamaciones
Inspirado de @ user1416920 Hice este:
function setData(key,val,obj)
{
keys = key.split(//./);
last = keys.pop();
keys.forEach(function(key)
{
if(typeof obj[key] === "undefined")
obj[key] = {};
obj = obj[key];
});
obj[last] = val;
}
Lo que hace es bastante auto explicativo ^^.
function setData(key,val,obj){
keys = key.split(//./);
to_set = keys.pop();
keys.forEach(function(obj_name){ obj = obj[obj_name]; });
obj[to_set] = val;
}