Javascript:如何使用数组给出的对象名称动态创建嵌套对象

Dav*_*vid 55 javascript arrays nested dynamic object

我希望有人可以帮助我使用这个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);
Run Code Online (Sandbox Code Playgroud)

...创建嵌套对象的部分正在执行此操作...

Settings["Modules"] = {};
Settings["Modules"]["Video"] = {};
Settings["Modules"]["Video"]["Plugin"] = "JWPlayer";
Run Code Online (Sandbox Code Playgroud)

但是,由于构成设置名称的部分数量可能会有所不同,例如newSettingName可能是"Modules_Floorplan_Image_Src",我想使用诸如...之类的函数动态执行此操作.

createSetting (newSettingNameArray, newSettingValue);

function createSetting(setting, value) {
    // code to create new setting goes here
}
Run Code Online (Sandbox Code Playgroud)

任何人都可以帮我解决如何动态地这样做吗?

我假设在那里必须有一个for循环才能通过数组进行迭代,但是我还没有找到一种方法来创建嵌套对象.

如果你有这么远,非常感谢你花时间阅读,即使你无法帮助.

jlg*_*all 90

放入一个函数,短而快(没有递归).

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.
Run Code Online (Sandbox Code Playgroud)

它会跳过层次结构中已存在的部分.如果您不确定是否已创建层次结构,则很有用.

要么:

一个更高级的版本,您可以直接将值分配给层次结构中的最后一个对象,并且您可以链接函数调用,因为它返回最后一个对象.

// 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
Run Code Online (Sandbox Code Playgroud)

注意:如果您的层次结构需要从标准对象以外的值构建(即不是{}),请参阅下面的TimDog答案.

编辑:使用常规循环而不是for...in循环.在库修改Array原型的情况下,它更安全.

  • @Bart:我不应该吗? (30认同)
  • 实际上,这是最好的答案,因为它的行为符合预期. (11认同)
  • 改善现有答案永远不会太晚. (8认同)
  • 我很荣幸对此进行了第 100 次投票。哈哈。 (2认同)

ken*_*ytm 66

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;
}
Run Code Online (Sandbox Code Playgroud)

用法:

var settings = {};
assign(settings, ['Modules', 'Video', 'Plugin'], 'JWPlayer');
Run Code Online (Sandbox Code Playgroud)

  • 好吧,我写了几乎相同的东西,除了我要解释`newSettingName.split('_')`。我看不到重复答案的意义,所以在那里。也许可以更好地解释您的答案。 (2认同)

Lau*_*ens 12

我的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;
};
Run Code Online (Sandbox Code Playgroud)

例:

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"}}}}
Run Code Online (Sandbox Code Playgroud)


ner*_*ist 8

另一个递归解决方案:

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;
};
Run Code Online (Sandbox Code Playgroud)

用法示例:

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!"}}
Run Code Online (Sandbox Code Playgroud)


小智 8

使用ES6的时间缩短了。将路径设置为数组。首先,您必须反转数组,才能开始填充对象。

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

const nestedObject = obj.reduce((prev, current) => (
    {[current]:{...prev}}
), {});
Run Code Online (Sandbox Code Playgroud)

  • 惊人的!如果之前定义了nestedObject并且设置了{a:{b:{}},但没有设置abc,那么如何更改它?即保留现有密钥并仅添加缺失的密钥。那真的很有用。 (2认同)

Vad*_*sov 7

我喜欢这种在嵌套字段上设置特定值的 ES6 不可变方式:

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

然后将其用于创建目标对象。

const targetObject = setValueToField(['one', 'two', 'three'], 'nice');
console.log(targetObject); // Output: { one: { two: { three: 'nice' } } }
Run Code Online (Sandbox Code Playgroud)


Der*_*Fan 7

Lodash 有一个_.set方法来实现这一点

let obj = {}

_.set(obj, ['a', 'b', 'c', 'd'], 'e')

or

_.set(obj, 'a.b.c.d', 'e')

// which generate the following object
{
   "a": {
      "b": {
         "c": {
            "d": "e"
         }
      }
   }
}
Run Code Online (Sandbox Code Playgroud)


Tim*_*Dog 5

这是对jlgrall的回答的简单调整,允许在嵌套层次结构中的每个元素上设置不同的值:

var createNestedObject = function( base, names, values ) {
    for( var i in names ) base = base[ names[i] ] = base[ names[i] ] || (values[i] || {});
};
Run Code Online (Sandbox Code Playgroud)

希望能帮助到你.


Ali*_*ich 5

这是动态创建嵌套对象的功能解决方案。

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);
}
Run Code Online (Sandbox Code Playgroud)

例子:

const data = {prop: 'someData'};
const path = 'a.deep.path';
const result = nest(path, data);
console.log(JSON.stringify(result));
// {"a":{"deep":{"path":{"prop":"someData"}}}}
Run Code Online (Sandbox Code Playgroud)