Javascript: как динамически создавать вложенные объекты, используя имена объектов, заданные массивом - PullRequest
53 голосов
/ 30 марта 2011

Я надеюсь, что кто-то может помочь мне с этим Javascript.

У меня есть объект под названием «Настройки», и я хотел бы написать функцию, которая добавляет новые настройки для этого объекта.

имя и значение новой настройки предоставляются в виде строк.Строка, дающая имя параметра, затем разделяется подчеркиванием на массив.Новый параметр должен быть добавлен к существующему объекту «Настройки» путем создания новых вложенных объектов с именами, заданными каждой частью массива, кроме последней части, которая должна быть строкой, дающей значение параметра.Затем я должен иметь возможность обратиться к настройке и, например, предупредить ее значение.Я могу сделать это статическим способом, как это ...

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);

... часть, которая создает вложенные объекты, делает это ...

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

Однако, какколичество частей, составляющих имя настройки, может варьироваться, например, newSettingName может быть «Modules_Floorplan_Image_Src», я хотел бы сделать это динамически, используя такую ​​функцию, как ...

createSetting (newSettingNameArray, newSettingValue);

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

Может кто-нибудь помочьмне решить, как сделать это динамически?

Я предполагаю, что должен быть цикл for ... для перехода через массив, но я не смог найти способ создатьвложенные объекты.

Если вы зашли так далеко, большое спасибо за то, что нашли время прочитать, даже если вы не можете помочь.

Ответы [ 17 ]

87 голосов
/ 11 июля 2012

Вставьте функцию, короткую и быструю (без рекурсии).

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.

Пропускает уже существующие части иерархии.Полезно, если вы не уверены, была ли иерархия уже создана.

Или:

Более интересная версия, в которой вы можете напрямую присвоить значение последнему объекту в иерархии, и выможет связывать вызовы функций, потому что он возвращает последний объект.

// 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

Примечание: если ваша иерархия должна быть построена из значений, отличных от стандартных объектов (т. е. не {}), см. также ответ TimDog ниже.

Редактировать: вместо обычных петель for...in используются обычные циклы.Это безопаснее в тех случаях, когда библиотека модифицирует прототип Array.

61 голосов
/ 30 марта 2011
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;
}

Использование:

var settings = {};
assign(settings, ['Modules', 'Video', 'Plugin'], 'JWPlayer');
12 голосов
/ 31 августа 2016

Мое решение ES2015. Сохраняет существующие значения.

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;
};

Пример:

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"}}}}
8 голосов
/ 26 февраля 2015

Еще одно рекурсивное решение:

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;
};

Пример использования:

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!"}}
7 голосов
/ 05 августа 2018

Использование ES6 сокращено.Установите свой путь в массив.во-первых, вы должны обратить массив, чтобы начать заполнение объекта.

let obj = ['a','b','c'] // {a:{b:{c:{}}}
obj.reverse();

const nestedObject = obj.reduce((prev, current) => (
    {[current]:{...prev}}
), {});
5 голосов
/ 30 октября 2012

Вот простая настройка ответа jlgrall, позволяющая установить отдельные значения для каждого элемента во вложенной иерархии:

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

Надеюсь, это поможет.

4 голосов
/ 12 февраля 2018

Вот функциональное решение для динамического создания вложенных объектов.

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);
}

Пример:

const data = {prop: 'someData'};
const path = 'a.deep.path';
const result = nest(path, data);
console.log(JSON.stringify(result));
// {"a":{"deep":{"path":{"prop":"someData"}}}}
1 голос
/ 20 августа 2015

Ценю, что этот вопрос мега старый! Но, столкнувшись с необходимостью сделать что-то подобное в узле, я создал модуль и опубликовал его в 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
1 голос
/ 20 июня 2018

Мне нравится этот неизменный способ ES6 установить определенное значение во вложенном поле:

const setValueToField = (fields, value) => {
  const reducer = (acc, item, index, arr) => ({ [item]: index + 1 < arr.length ? acc : value });
  return fields.reduceRight(reducer, {});
};

, а затем использовать его для создания целевого объекта.

const targetObject = setValueToField(['one', 'two', 'three'], 'nice');
console.log(targetObject); // Output: { one: { two: { three: 'nice' } } }
0 голосов
/ 11 августа 2017

Попробуйте это: https://github.com/silkyland/object-to-formdata

var obj2fd = require('obj2fd/es5').default
var fd = obj2fd({
             a:1,
             b:[
                {c: 3},
                {d: 4}
             ]
})

Результат:

fd = [
       a => 1,
       b => [
         c => 3,
         d => 4
       ]
]
...