2011-03-30 19 views
39

Espero que alguien me ayude con este Javascript.Javascript: cómo crear dinámicamente objetos anidados utilizando nombres de objetos dados por una matriz

Tengo un objeto llamado "Configuración" y me gustaría escribir una función que agregue nuevas configuraciones a ese objeto.

El nuevo nombre y valor de la configuración se proporcionan como cadenas. La cadena que da el nombre de la configuración se divide por los guiones bajos en una matriz. La nueva configuración debe agregarse al objeto "Configuraciones" existente creando nuevos objetos anidados con los nombres dados por cada parte de la matriz, excepto la última parte que debe ser una cadena que proporcione el valor de la configuración. Entonces debería ser capaz de referirme a la configuración y, p. alerta su valor Puedo hacer esto de una manera estática como esto ...

var Settings = {}; 
var newSettingName = "Modules_Video_Plugin"; 
var newSettingValue = "JWPlayer"; 
var newSettingNameArray = newSettingName.split("_"); 

Settings[newSettingNameArray[0]] = {}; 
Settings[newSettingNameArray[0]][newSettingNameArray[1]] = {}; 
Settings[newSettingNameArray[0]][newSettingNameArray[1]][newSettingNameArray[2]] = newSettingValue; 

alert(Settings.Modules.Mediaplayers.Video.Plugin); 

... la parte que crea los objetos anidados está haciendo esto ...

Settings["Modules"] = {}; 
Settings["Modules"]["Video"] = {}; 
Settings["Modules"]["Video"]["Plugin"] = "JWPlayer"; 

Sin embargo, como el número de piezas que componen el nombre de la configuración puede variar, por ejemplo un newSettingName podría ser "Modules_Floorplan_Image_Src", me gustaría hacer esto de forma dinámica utilizando una función como ...

createSetting (newSettingNameArray, newSettingValue); 

function createSetting(setting, value) { 
    // code to create new setting goes here 
} 

Puede alguien ayudarme a encontrar la manera de hacer esto de forma dinámica?

Supongo que tiene que haber un ... ciclo para iterar a través de la matriz, pero no he podido encontrar la forma de crear los objetos anidados.

Si ha llegado hasta aquí, muchas gracias por tomarse el tiempo para leer, incluso si no puede ayudar.

Respuesta

47
function assign(obj, keyPath, value) { 
    lastKeyIndex = keyPath.length-1; 
    for (var i = 0; i < lastKeyIndex; ++ i) { 
    key = keyPath[i]; 
    if (!(key in obj)) 
     obj[key] = {} 
    obj = obj[key]; 
    } 
    obj[keyPath[lastKeyIndex]] = value; 
} 

Uso:

var settings = {}; 
assign(settings, ['Modules', 'Video', 'Plugin'], 'JWPlayer'); 
+1

Bueno, escribió más o menos la misma cosa, excepto que iba a explicar 'newSettingName.split ('_')'. No veo el punto detrás de las respuestas duplicadas, entonces ahí. Sin embargo, tal vez explique mejor su respuesta. – Christian

+0

¿qué son exactamente KeyPath y valor en esa función? –

+0

Excelente. Fui al último valor y, por lo tanto, fallé. Entendido, buscado y encontrado esto. Estar a nivel de padre es la clave y funciona bien. –

0

tratar de usar la función recursiva:

function createSetting(setting, value, index) { 
    if (typeof index !== 'number') { 
    index = 0; 
    } 

    if (index+1 == setting.length) { 
    settings[setting[index]] = value; 
    } 
    else { 
    settings[setting[index]] = {}; 
    createSetting(setting, value, ++index); 
    } 
} 
0

pienso, esto es más corto:

Settings = {}; 
newSettingName = "Modules_Floorplan_Image_Src"; 
newSettingValue = "JWPlayer"; 
newSettingNameArray = newSettingName.split("_"); 

a = Settings; 
for (var i = 0 in newSettingNameArray) { 
    var x = newSettingNameArray[i]; 
    a[x] = i == newSettingNameArray.length-1 ? newSettingValue : {}; 
    a = a[x]; 
} 
71

Poner en una función, corto y rápido (sin recursividad).

var createNestedObject = function(base, names) { 
    for(var i = 0; i < names.length; i++) { 
     base = base[ names[i] ] = base[ names[i] ] || {}; 
    } 
}; 

// Usage: 
createNestedObject(window, ["shapes", "triangle", "points"]); 
// Now window.shapes.triangle.points is an empty object, ready to be used. 

Salta las partes ya existentes de la jerarquía. Útil si no está seguro de si la jerarquía ya se creó.

O:

Una versión más elegante donde se puede asignar directamente el valor para el último objeto en la jerarquía, y se puede llama función de la cadena, ya que devuelve el último objeto.

// Function: createNestedObject(base, names[, value]) 
// base: the object on which to create the hierarchy 
// names: an array of strings contaning the names of the objects 
// value (optional): if given, will be the last object in the hierarchy 
// Returns: the last object in the hierarchy 
var createNestedObject = function(base, names, value) { 
    // If a value is given, remove the last name and keep it for later: 
    var lastName = arguments.length === 3 ? names.pop() : false; 

    // Walk the hierarchy, creating new objects where needed. 
    // If the lastName was removed, then the last object is not set yet: 
    for(var i = 0; i < names.length; i++) { 
     base = base[ names[i] ] = base[ names[i] ] || {}; 
    } 

    // If a value was given, set it to the last name: 
    if(lastName) base = base[ lastName ] = value; 

    // Return the last object in the hierarchy: 
    return base; 
}; 

// Usages: 

createNestedObject(window, ["shapes", "circle"]); 
// Now window.shapes.circle is an empty object, ready to be used. 

var obj = {}; // Works with any object other that window too 
createNestedObject(obj, ["shapes", "rectangle", "width"], 300); 
// Now we have: obj.shapes.rectangle.width === 300 

createNestedObject(obj, "shapes.rectangle.height".split('.'), 400); 
// Now we have: obj.shapes.rectangle.height === 400 

Nota: si su jerarquía tiene que ser construido a partir de valores de otro tipo que los objetos estándar, véase también la respuesta de TimDog a continuación (es decir, no {}.).

Editar: utiliza bucles regulares en lugar de for...in bucles. Es más seguro en los casos en que una biblioteca modifica el prototipo de matriz.

+0

Espero que se dé cuenta de que está respondiendo una pregunta cuya respuesta aceptada ya está más de 15 meses de edad ... – Bart

+21

@Bart: ¿No debería? – jlgrall

+0

Está bien, no es muy probable que la persona que hizo la pregunta todavía esté buscando una respuesta. Su respuesta sigue siendo buena (y diferente del resto), así que le di algunos pulgares arriba. – Bart

4

Aquí es un pellizco simple de la respuesta de jlgrall que permite establecer distintos valores en cada elemento de la jerarquía anidada:

var createNestedObject = function(base, names, values) { 
    for(var i in names) base = base[ names[i] ] = base[ names[i] ] || (values[i] || {}); 
}; 

espero que ayude.

+1

¿Por qué funciona esto y no elimina los objetos anidados existentes? Usted ha volado las mentes, señor. –

+1

¿Puede explicar cómo funciona exactamente? Plz .. – Pratik

+0

Sé que esto es viejo, pero pensé que me gustaría entrar. La razón por la que esto funciona es que el argumento "base" se pasa por una copia de la referencia al objeto original. Entonces, en cada iteración evaluada desde la base de derecha a izquierda se convierte en un puntero a la posición de la propiedad actualmente asignada. – chrisw

0

Encontré que la respuesta de @ jlgrall fue excelente, pero después de simplificarla, no funcionó en Chrome. Aquí está mi fijo si alguien quiere una versión lite:

var callback = 'fn.item1.item2.callbackfunction', 
    cb = callback.split('.'), 
    baseObj = window; 

function createNestedObject(base, items){ 
    $.each(items, function(i, v){ 
     base = base[v] = (base[v] || {}); 
    }); 
} 

callbackFunction = createNestedObject(baseObj, cb); 

console.log(callbackFunction); 

Espero que esto sea útil y relevante. Lo sentimos, acabo rompieron este ejemplo fuera ...

8

Otra solución recursiva:

var nest = function(obj, keys, v) { 
    if (keys.length === 1) { 
     obj[keys[0]] = v; 
    } else { 
     var key = keys.shift(); 
     obj[key] = nest(typeof obj[key] === 'undefined' ? {} : obj[key], keys, v); 
    } 

    return obj; 
}; 

Ejemplo de uso:

var dog = {bark: {sound: 'bark!'}}; 
nest(dog, ['bark', 'loudness'], 66); 
nest(dog, ['woff', 'sound'], 'woff!'); 
console.log(dog); // {bark: {loudness: 66, sound: "bark!"}, woff: {sound: "woff!"}} 
0

puede definir sus propios métodos de objeto; También estoy usando subrayados por razones de brevedad: el uso

var _ = require('underscore'); 

// a fast get method for object, by specifying an address with depth 
Object.prototype.pick = function(addr) { 
    if (!_.isArray(addr)) return this[addr]; // if isn't array, just get normally 
    var tmpo = this; 
    while (i = addr.shift()) 
     tmpo = tmpo[i]; 
    return tmpo; 
}; 
// a fast set method for object, put value at obj[addr] 
Object.prototype.put = function(addr, val) { 
    if (!_.isArray(addr)) this[addr] = val; // if isn't array, just set normally 
    this.pick(_.initial(addr))[_.last(addr)] = val; 
}; 

muestra:

var obj = { 
      'foo': { 
        'bar': 0 }} 

obj.pick('foo'); // returns { bar: 0 } 
obj.pick(['foo','bar']); // returns 0 
obj.put(['foo', 'bar'], -1) // obj becomes {'foo': {'bar': -1}} 
0

aprecian que esta pregunta es Mega edad! Pero después de encontrar la necesidad de hacer algo como esto en un nodo, hice un módulo y lo publiqué en npm. Nestob

var nestob = require('nestob'); 

//Create a new nestable object - instead of the standard js object ({}) 
var newNested = new nestob.Nestable(); 

//Set nested object properties without having to create the objects first! 
newNested.setNested('biscuits.oblong.marmaduke', 'cheese'); 
newNested.setNested(['orange', 'tartan', 'pipedream'], { poppers: 'astray', numbers: [123,456,789]}); 

console.log(newNested, newNested.orange.tartan.pipedream); 
//{ biscuits: { oblong: { marmaduke: 'cheese' } }, 
    orange: { tartan: { pipedream: [Object] } } } { poppers: 'astray', numbers: [ 123, 456, 789 ] } 

//Get nested object properties without having to worry about whether the objects exist 
//Pass in a default value to be returned if desired 
console.log(newNested.getNested('generic.yoghurt.asguard', 'autodrome')); 
//autodrome 

//You can also pass in an array containing the object keys 
console.log(newNested.getNested(['chosp', 'umbridge', 'dollar'], 'symbols')); 
//symbols 

//You can also use nestob to modify objects not created using nestob 
var normalObj = {}; 

nestob.setNested(normalObj, 'running.out.of', 'words'); 

console.log(normalObj); 
//{ running: { out: { of: 'words' } } } 

console.log(nestob.getNested(normalObj, 'random.things', 'indigo')); 
//indigo 
console.log(nestob.getNested(normalObj, 'improbable.apricots')); 
//false 
0

Un fragmento para aquellos que necesitan para crear un objetos anidados con apoyo de claves de matriz para establecer un valor al final del camino. La ruta es la cadena como: modal.product.action.review.2.write.survey.data. Basado en la versión jlgrall.

var updateStateQuery = function(state, path, value) { 
    var names = path.split('.'); 
    for (var i = 0, len = names.length; i < len; i++) { 
     if (i == (len - 1)) { 
      state = state[names[i]] = state[names[i]] || value; 
     } 
     else if (parseInt(names[i+1]) >= 0) { 
      state = state[names[i]] = state[names[i]] || []; 
     } 
     else { 
      state = state[names[i]] = state[names[i]] || {}; 
     } 
    } 
}; 
0

conjunto anidado de datos:

function setNestedData(root, path, value) { 
    var paths = path.split('.'); 
    var last_index = paths.length - 1; 
    paths.forEach(function(key, index) { 
    if (!(key in root)) root[key] = {}; 
    if (index==last_index) root[key] = value; 
    root = root[key]; 
    }); 
    return root; 
} 

var obj = {'existing': 'value'}; 
setNestedData(obj, 'animal.fish.pet', 'derp'); 
setNestedData(obj, 'animal.cat.pet', 'musubi'); 
console.log(JSON.stringify(obj)); 
// {"existing":"value","animal":{"fish":{"pet":"derp"},"cat":{"pet":"musubi"}}} 

Obtener datos anidados:

function getNestedData(obj, path) { 
    var index = function(obj, i) { return obj && obj[i]; }; 
    return path.split('.').reduce(index, obj); 
} 
getNestedData(obj, 'animal.cat.pet') 
// "musubi" 
getNestedData(obj, 'animal.dog.pet') 
// undefined 
6

Mi solución ES2015. Mantiene los valores existentes.

const set = (obj, path, val) => { 
    const keys = path.split('.'); 
    const lastKey = keys.pop(); 
    const lastObj = keys.reduce((obj, key) => 
     obj[key] = obj[key] || {}, 
     obj); 
    lastObj[lastKey] = val; 
}; 

Ejemplo:

const obj = {'a': {'prop': {'that': 'exists'}}}; 
set(obj, 'a.very.deep.prop', 'value'); 
console.log(JSON.stringify(obj)); 
// {"a":{"prop":{"that":"exists"},"very":{"deep":{"prop":"value"}}}} 
0

Eval es algo excesivo, pero el resultado es fácil de visualizar, sin bucles anidados o recursividad.

function buildDir(obj, path){ 
    var paths = path.split('_'); 
    var final = paths.pop(); 
    for (let i = 1; i <= paths.length; i++) { 
    var key = "obj['" + paths.slice(0, i).join("']['") + "']" 
    console.log(key) 
    eval(`${key} = {}`) 
    } 
    eval(`${key} = '${final}'`) 
    return obj 
} 

var newSettingName = "Modules_Video_Plugin_JWPlayer"; 
var Settings = buildDir({}, newSettingName); 

Básicamente usted está escribiendo progresivamente una cadena "obj['one']= {}", "obj['one']['two']"= {} y evaling ella;

1

Aquí es una solución funcional para crear dinámicamente objetos anidados.

const nest = (path, obj) => { 
    const reversedPath = path.split('.').reverse(); 

    const iter = ([head, ...tail], obj) => { 
    if (!head) { 
     return obj; 
    } 
    const newObj = {[head]: {...obj}}; 
    return iter(tail, newObj); 
    } 
    return iter(reversedPath, obj); 
} 

Ejemplo:

const data = {prop: 'someData'}; 
const path = 'a.deep.path'; 
const result = nest(path, data); 
console.log(JSON.stringify(result)); 
// {"a":{"deep":{"path":{"prop":"someData"}}}} 
Cuestiones relacionadas