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

IT技术 javascript arrays object dynamic nested
2021-02-08 09:18:14

我希望有人可以帮助我使用这个 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... 循环来遍历数组,但我无法找到一种方法来创建嵌套对象。

如果你已经读到这里,非常感谢你花时间阅读,即使你无能为力。

6个回答

放入一个函数,简短而快速(无递归)。

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

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

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

@jlgrall如果我需要从另一个处理实际值分配的函数调用它,有没有办法返回新的键路径而不是键的值(基)?
2021-03-15 09:18:14
没关系,只是提出问题的人不太可能仍在寻找答案。你的答案仍然很好(与其他答案不同)所以我给了你一些赞许。
2021-03-19 09:18:14
我希望你意识到你正在回答一个被接受的答案已经超过 15 个月的问题......
2021-03-25 09:18:14
实际上,这是最好的答案,因为它的行为符合预期。
2021-03-25 09:18:14
改进现有答案永远不会太晚。
2021-04-11 09:18:14
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');
该函数中的 keyPath 和 value 到底是什么?
2021-03-17 09:18:14
嗯,我写了几乎相同的东西,只是我要解释一下newSettingName.split('_')我没有看到重复答案背后的意义,所以在那里。不过,也许可以更好地解释您的答案。
2021-03-18 09:18:14
上面例子中的keyPath,意思是,key是Modules.Video.Plugin,value是JWPlayer,在他们试图构建的json中
2021-03-20 09:18:14
伟大的一个。我确实去了最后一个值,因此失败了。了解,搜索并找到了这个。处于父母级别是关键并且工作正常。
2021-04-05 09:18:14

我的 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"}}}}

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

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

const nestedObject = obj.reduce((prev, current) => (
    {[current]:{...prev}}
), {});
惊人的!如果之前定义了nestedObject 并且设置了{a: {b:{}},但没有设置abc,那么如何更改?即保留现有的键,只添加缺失。那真的很有用。
2021-03-18 09:18:14

另一个递归解决方案:

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!"}}