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

发布于 2024-10-28 04:32:23 字数 1228 浏览 6 评论 0原文

我希望有人能帮我解决这个 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...循环来遍历数组,但我无法找到创建嵌套对象的方法。

如果您已经读到这里,非常感谢您花时间阅读,即使您帮不上忙。

I hope someone can help me with this Javascript.

I have an Object called "Settings" and I would like to write a function that adds new settings to that object.

The new setting's name and value are provided as strings. The string giving the setting's name is then split by the underscores into an array. The new setting should get added to the existing "Settings" object by creating new nested objects with the names given by each part of the array, except the last part which should be a string giving the setting's value. I should then be able to refer to the setting and e.g. alert its value. I can do this in a static way like this...

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

... the part that creates the nested objects is doing this ...

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

However, as the number of parts that make up the setting name can vary, e.g. a newSettingName could be "Modules_Floorplan_Image_Src", I'd like to do this dynamically using a function such as...

createSetting (newSettingNameArray, newSettingValue);

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

Can anyone help me work out how to do this dynamically?

I presume there has to be a for...loop in there to itterate through the array, but I haven't been able to work out a way to create the nested objects.

If you've got this far thanks very much for taking the time to read even if you can't help.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(26

伤感在游骋 2024-11-04 04:32:23

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

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 循环。在库修改数组原型的情况下更安全。

Put in a function, short and fast (no recursion).

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.

It skips already existing parts of the hierarchy. Useful if you are not sure whether the hierarchy was already created.

Or:

A fancier version where you can directly assign the value to the last object in the hierarchy, and you can chain function calls because it returns the last object.

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

Note: if your hierarchy needs to be built from values other that standard objects (ie. not {}), see also TimDog's answer below.

Edit: uses regular loops instead of for...in loops. It's safer in cases where a library modifies the Array prototype.

_畞蕅 2024-11-04 04:32:23
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');
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;
}

Usage:

var settings = {};
assign(settings, ['Modules', 'Video', 'Plugin'], 'JWPlayer');
薄暮涼年 2024-11-04 04:32:23

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

My ES2015 solution. Keeps existing values.

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

Example:

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"}}}}
仲春光 2024-11-04 04:32:23

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

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

const nestedObject = obj.reduce((prev, current) => (
    {[current]:{...prev}}
), {});

Using ES6 is shorten. Set your path into an array.
first, you have to reverse the array, to start filling the object.

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

const nestedObject = obj.reduce((prev, current) => (
    {[current]:{...prev}}
), {});
屌丝范 2024-11-04 04:32:23

另一种递归解决方案:

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

Another recursive solution:

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

Example usage:

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!"}}
追我者格杀勿论 2024-11-04 04:32:23

我喜欢这种在嵌套字段上设置特定值的 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' } } }

I love this ES6 immutable way to set certain value on nested field:

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

And then use it with creating your target object.

const targetObject = setValueToField(['one', 'two', 'three'], 'nice');
console.log(targetObject); // Output: { one: { two: { three: 'nice' } } }
一页 2024-11-04 04:32:23

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

Lodash has a _.set method to achieve this

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"
         }
      }
   }
}
自由如风 2024-11-04 04:32:23

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

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

希望它有所帮助。

Here is a simple tweak to jlgrall's answer that allows setting distinct values on each element in the nested hierarchy:

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

Hope it helps.

々眼睛长脚气 2024-11-04 04:32:23

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

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

Here is a functional solution to dynamically create nested objects.

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

Example:

const data = {prop: 'someData'};
const path = 'a.deep.path';
const result = nest(path, data);
console.log(JSON.stringify(result));
// {"a":{"deep":{"path":{"prop":"someData"}}}}
红衣飘飘貌似仙 2024-11-04 04:32:23

受到 ImmutableJS setIn 方法的启发,该方法永远不会改变原始方法。这适用于混合数组和对象嵌套值。

function setIn(obj = {}, [prop, ...rest], value) {
    const newObj = Array.isArray(obj) ? [...obj] : {...obj};
    newObj[prop] = rest.length ? setIn(obj[prop], rest, value) : value;
    return newObj;
}

var obj = {
  a: {
    b: {
      c: [
        {d: 5}
      ]
    }
  }
};

const newObj = setIn(obj, ["a", "b", "c", 0, "x"], "new");

//obj === {a: {b: {c: [{d: 5}]}}}
//newObj === {a: {b: {c: [{d: 5, x: "new"}]}}}

Inspired by ImmutableJS setIn method which will never mutate the original. This works with mixed array and object nested values.

function setIn(obj = {}, [prop, ...rest], value) {
    const newObj = Array.isArray(obj) ? [...obj] : {...obj};
    newObj[prop] = rest.length ? setIn(obj[prop], rest, value) : value;
    return newObj;
}

var obj = {
  a: {
    b: {
      c: [
        {d: 5}
      ]
    }
  }
};

const newObj = setIn(obj, ["a", "b", "c", 0, "x"], "new");

//obj === {a: {b: {c: [{d: 5}]}}}
//newObj === {a: {b: {c: [{d: 5, x: "new"}]}}}
巴黎盛开的樱花 2024-11-04 04:32:23

感谢这个问题是非常古老的!但是在遇到需要在 Node 中执行类似操作后,我制作了一个模块并将其发布到 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

Appreciate that this question is mega old! But after coming across a need to do something like this in node, I made a module and published it to 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
空气里的味道 2024-11-04 04:32:23

在循环中,您可以使用 lodash.set 并为您创建路径:

...
const set = require('lodash.set');

const p = {};
const [type, lang, name] = f.split('.');
set(p, [lang, type, name], '');

console.log(p);
// { lang: { 'type': { 'name': '' }}}

Inside your loop you can use lodash.set and will create the path for you:

...
const set = require('lodash.set');

const p = {};
const [type, lang, name] = f.split('.');
set(p, [lang, type, name], '');

console.log(p);
// { lang: { 'type': { 'name': '' }}}
甜嗑 2024-11-04 04:32:23

尝试使用递归函数:

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

try using recursive function:

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);
  }
}
放手` 2024-11-04 04:32:23

我认为,这更短:

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

I think, this is shorter:

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];
}
杀お生予夺 2024-11-04 04:32:23

我发现 @jlgrall 的答案很好,但简化后,它在 Chrome 中不起作用。如果有人想要精简版,这是我的修复:

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

我希望这是有用且相关的。抱歉,我刚刚把这个例子搞砸了......

I found @jlgrall's answer was great but after simplifying it, it didn't work in Chrome. Here's my fixed should anyone want a lite version:

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

I hope this is useful and relevant. Sorry, I've just smashed this example out...

饮湿 2024-11-04 04:32:23

您可以定义自己的对象方法;为了简洁起见,我还使用下划线:

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

示例用法:

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

You can define your own Object methods; also I'm using underscore for brevity:

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

Sample usage:

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}}
姜生凉生 2024-11-04 04:32:23

为那些需要创建嵌套对象并支持数组键以将值设置为路径末尾的人提供的代码片段。路径是如下字符串:modal.product.action.review.2.write.survey.data。基于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]] || {};
        }
    }
};

A snippet for those who need to create a nested objects with support of array keys to set a value to the end of path. Path is the string like: modal.product.action.review.2.write.survey.data. Based on jlgrall version.

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]] || {};
        }
    }
};
擦肩而过的背影 2024-11-04 04:32:23

设置嵌套数据:

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

获取嵌套数据:

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

Set Nested Data:

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

Get Nested Data:

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
绿萝 2024-11-04 04:32:23

试试这个: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
       ]
]

Try this: https://github.com/silkyland/object-to-formdata

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

Result :

fd = [
       a => 1,
       b => [
         c => 3,
         d => 4
       ]
]
深居我梦 2024-11-04 04:32:23

下面是几个有用函数的分解,每个函数都保存现有数据。不处理数组。

  • setDeep:回答问题。对对象中的其他数据无损。
  • setDefaultDeep:相同,但仅在尚未设置时设置。
  • setDefault:如果尚未设置,则设置一个键。与 Python 的 setdefault 相同。
  • setStructure:构建路径的辅助函数。
// Create a nested structure of objects along path within obj. Only overwrites the final value.
let setDeep = (obj, path, value) =>
    setStructure(obj, path.slice(0, -1))[path[path.length - 1]] = value

// Create a nested structure of objects along path within obj. Does not overwrite any value.
let setDefaultDeep = (obj, path, value) =>
    setDefault(setStructure(obj, path.slice(0, -1)), path[path.length - 1], value)

// Set obj[key] to value if key is not in object, and return obj[key]
let setDefault = (obj, key, value) =>
    obj[key] = key in obj ? obj[key] : value;

// Create a nested structure of objects along path within obj. Does not overwrite any value.
let setStructure = (obj, path) => 
    path.reduce((obj, segment) => setDefault(obj, segment, {}), obj);



// EXAMPLES
let temp = {};

// returns the set value, similar to assignment
console.log('temp.a.b.c.d:', 
            setDeep(temp, ['a', 'b', 'c', 'd'], 'one'))

// not destructive to 'one'
setDeep(temp, ['a', 'b', 'z'], 'two')

// does not overwrite, returns previously set value
console.log('temp.a.b.z:  ', 
            setDefaultDeep(temp, ['a', 'b', 'z'], 'unused'))

// creates new, returns current value
console.log('temp["a.1"]: ', 
            setDefault(temp, 'a.1', 'three'))

// can also be used as a getter
console.log("temp.x.y.z:  ", 
            setStructure(temp, ['x', 'y', 'z']))


console.log("final object:", temp)

我不确定为什么有人会想要字符串路径:

  1. 它们对于带有句点的键来说是不明确的
  2. 你必须首先构建字符串

Here is a decomposition to several useful functions, that each preserve existing data. Does not handle arrays.

  • setDeep: Answers question. Non-destructive to other data in the object.
  • setDefaultDeep: Same, but only sets if not already set.
  • setDefault: Sets a key if not already set. Same as Python's setdefault.
  • setStructure: Helper function that builds the path.

// Create a nested structure of objects along path within obj. Only overwrites the final value.
let setDeep = (obj, path, value) =>
    setStructure(obj, path.slice(0, -1))[path[path.length - 1]] = value

// Create a nested structure of objects along path within obj. Does not overwrite any value.
let setDefaultDeep = (obj, path, value) =>
    setDefault(setStructure(obj, path.slice(0, -1)), path[path.length - 1], value)

// Set obj[key] to value if key is not in object, and return obj[key]
let setDefault = (obj, key, value) =>
    obj[key] = key in obj ? obj[key] : value;

// Create a nested structure of objects along path within obj. Does not overwrite any value.
let setStructure = (obj, path) => 
    path.reduce((obj, segment) => setDefault(obj, segment, {}), obj);



// EXAMPLES
let temp = {};

// returns the set value, similar to assignment
console.log('temp.a.b.c.d:', 
            setDeep(temp, ['a', 'b', 'c', 'd'], 'one'))

// not destructive to 'one'
setDeep(temp, ['a', 'b', 'z'], 'two')

// does not overwrite, returns previously set value
console.log('temp.a.b.z:  ', 
            setDefaultDeep(temp, ['a', 'b', 'z'], 'unused'))

// creates new, returns current value
console.log('temp["a.1"]: ', 
            setDefault(temp, 'a.1', 'three'))

// can also be used as a getter
console.log("temp.x.y.z:  ", 
            setStructure(temp, ['x', 'y', 'z']))


console.log("final object:", temp)

I'm not sure why anyone would want string paths:

  1. They are ambiguous for keys with periods
  2. You have to build the strings in the first place
孤独患者 2024-11-04 04:32:23

由于我从本页开始,所以我想回馈

其他示例,即使它已设置,其他示例也会覆盖最终节点,而这不是我想要的。

另外,如果returnObj设置为true,它会返回基础对象。默认情况下,falsy,它返回最深的节点。

function param(obj, path, value, returnObj) {
  if (typeof path == 'string') path = path.split(".");
  var child = obj;
  path.forEach((key, i) => {
    if (!(key in child)) {
      child[key] = (i < path.length-1) ? {} : value || {};
    }
    child = child[key];
  });
  return returnObj ? obj : child;
}

var x = {};
var xOut = param(x, "y.z", "setting")
console.log(xOut);
xOut = param(x, "y.z", "overwrite") // won't set
console.log(xOut);
xOut = param(x, "y.a", "setting2")
console.log(xOut);
xOut = param(x, "y.a", "setting2", true) // get object rather than deepest node.
console.log(xOut);

您还可以执行将数字键放置在数组中的操作(如果它们尚不存在)。请注意,数字键不会转换为路径第一个元素的数组,因为它是由基础对象的类型设置的。

function isNumber(n) {
  return !isNaN(parseFloat(n)) && isFinite(n);
}

function param(obj, path, value, returnObj) {
  if (typeof path == 'string') path = path.split(".");
  var child = obj;
  path.forEach((key, i) => {
    var nextKey = path[i+1];
    if (!(key in child)) {
      child[key] = (nextKey == undefined && value != undefined 
        ? value 
        : isNumber(nextKey)
          ? []
          : {});
    }
    child = child[key];
  });
  return returnObj ? obj : child;
}

var x = {};

var xOut = param(x, "y.z", "setting")
console.log(xOut);
xOut = param(x, "y.z", "overwrite") // won't set
console.log(xOut);
xOut = param(x, "y.a", "setting2")
console.log(xOut);
xOut = param(x, "y.a", "setting2", true) // get object rather than deepest node.
xOut = param(x, "1.0.2.a", "setting")
xOut = param(x, "1.0.1.a", "try to override") // won't set
xOut = param(x, "1.0.5.a", "new-setting", true) // get object rather than deepest node.
console.log(xOut);

当然,当数字键大于 0 时,您可能会看到一些未定义间隙。

实际用途可能是

function AddNote(book, page, line) {
  // assume a global global notes collection
  var myNotes = param(allNotes, [book, page, line], []);
  myNotes.push('This was a great twist!')
  return myNotes;
}

var allNotes = {}
var youthfulHopes = AddNote('A Game of Thrones', 4, 2, "I'm already hooked, at least I won't have to wait long for the books to come out!");

console.log(allNotes)
// {"A Game of Thrones": [undefined, undefined, undefined, undefined, [undefined, undefined, ["I'm already hooked, at least I won't have to wait long for the books to come out!"]]]}
console.log(youthfulHopes)
// ["I'm already hooked, at least I won't have to wait long for the books to come out!"]

Since I started with something from this page, I wanted to contribute back

Other examples overwrote the final node even if it was set, and that wasn't what I wanted.

Also, if returnObj is set to true, it returns the base object. By default, falsy, it returns the deepest node.

function param(obj, path, value, returnObj) {
  if (typeof path == 'string') path = path.split(".");
  var child = obj;
  path.forEach((key, i) => {
    if (!(key in child)) {
      child[key] = (i < path.length-1) ? {} : value || {};
    }
    child = child[key];
  });
  return returnObj ? obj : child;
}

var x = {};
var xOut = param(x, "y.z", "setting")
console.log(xOut);
xOut = param(x, "y.z", "overwrite") // won't set
console.log(xOut);
xOut = param(x, "y.a", "setting2")
console.log(xOut);
xOut = param(x, "y.a", "setting2", true) // get object rather than deepest node.
console.log(xOut);

You can also do something where numeric keys are placed in arrays (if they don't already exist). Note that numeric keys won't convert to arrays for the first element of the path, since that's set by the type of your base-object.

function isNumber(n) {
  return !isNaN(parseFloat(n)) && isFinite(n);
}

function param(obj, path, value, returnObj) {
  if (typeof path == 'string') path = path.split(".");
  var child = obj;
  path.forEach((key, i) => {
    var nextKey = path[i+1];
    if (!(key in child)) {
      child[key] = (nextKey == undefined && value != undefined 
        ? value 
        : isNumber(nextKey)
          ? []
          : {});
    }
    child = child[key];
  });
  return returnObj ? obj : child;
}

var x = {};

var xOut = param(x, "y.z", "setting")
console.log(xOut);
xOut = param(x, "y.z", "overwrite") // won't set
console.log(xOut);
xOut = param(x, "y.a", "setting2")
console.log(xOut);
xOut = param(x, "y.a", "setting2", true) // get object rather than deepest node.
xOut = param(x, "1.0.2.a", "setting")
xOut = param(x, "1.0.1.a", "try to override") // won't set
xOut = param(x, "1.0.5.a", "new-setting", true) // get object rather than deepest node.
console.log(xOut);

Naturally, when the numeric keys are greater than 0, you might see some undefined gaps.

Practical uses of this might be

function AddNote(book, page, line) {
  // assume a global global notes collection
  var myNotes = param(allNotes, [book, page, line], []);
  myNotes.push('This was a great twist!')
  return myNotes;
}

var allNotes = {}
var youthfulHopes = AddNote('A Game of Thrones', 4, 2, "I'm already hooked, at least I won't have to wait long for the books to come out!");

console.log(allNotes)
// {"A Game of Thrones": [undefined, undefined, undefined, undefined, [undefined, undefined, ["I'm already hooked, at least I won't have to wait long for the books to come out!"]]]}
console.log(youthfulHopes)
// ["I'm already hooked, at least I won't have to wait long for the books to come out!"]
绻影浮沉 2024-11-04 04:32:23
function initPath(obj, path) {
  path.split('.').reduce((o, key) => (
    Object.assign(o, {[key]: Object(o[key])}),
    o[key]
  ), obj);
  return obj;
}

用法

const obj = { a: { b: 'value1' } };
initPath(obj, 'a.c.d').a.c.d='value2';
/*
{
  "a": {
    "b": "value1",
    "c": {
      "d": "value2"
    }
  }
}
*/
function initPath(obj, path) {
  path.split('.').reduce((o, key) => (
    Object.assign(o, {[key]: Object(o[key])}),
    o[key]
  ), obj);
  return obj;
}

Usage

const obj = { a: { b: 'value1' } };
initPath(obj, 'a.c.d').a.c.d='value2';
/*
{
  "a": {
    "b": "value1",
    "c": {
      "d": "value2"
    }
  }
}
*/
网名女生简单气质 2024-11-04 04:32:23

简单的答案。在 es6 上,我使用此

const assign = (obj, path, value) => {
  let keyPath = path.split('.')
  let lastKeyIndex = keyPath.length - 1
  for (let i = 0; i < lastKeyIndex; ++i) {
    let key = keyPath[i]
    if (!(key in obj)) {
      obj[key] = {}
    }
    obj = obj[key]
  }
  obj[keyPath[lastKeyIndex]] = value
}

示例 json,

const obj = {
  b: 'hello'
}

您可以添加新密钥

assign(obj, 'c.d.e', 'this value')

,如下所示

console.log(obj)
//response example
obj = {
   b: 'hello',
   c: {
      d: {
         e: 'this value'
      }
   }
}

simple answer. on es6, im using this

const assign = (obj, path, value) => {
  let keyPath = path.split('.')
  let lastKeyIndex = keyPath.length - 1
  for (let i = 0; i < lastKeyIndex; ++i) {
    let key = keyPath[i]
    if (!(key in obj)) {
      obj[key] = {}
    }
    obj = obj[key]
  }
  obj[keyPath[lastKeyIndex]] = value
}

example json

const obj = {
  b: 'hello'
}

you can add new key

assign(obj, 'c.d.e', 'this value')

and you get like bellow

console.log(obj)
//response example
obj = {
   b: 'hello',
   c: {
      d: {
         e: 'this value'
      }
   }
}
我是有多爱你 2024-11-04 04:32:23
function createObj(keys, value) {
  let obj = {}
  let schema = obj
  keys = keys.split('.')

  for (let i = 0; i < keys.length - 1; i++) {
    schema[keys[i]] = {}
    schema = schema[keys[i]]
  }

  schema[keys.pop()] = value

  return obj
}


let keys = 'value1.value2.value3'
let value = 'Hello'

let obj = createObj(keys, value)

function createObj(keys, value) {
  let obj = {}
  let schema = obj
  keys = keys.split('.')

  for (let i = 0; i < keys.length - 1; i++) {
    schema[keys[i]] = {}
    schema = schema[keys[i]]
  }

  schema[keys.pop()] = value

  return obj
}


let keys = 'value1.value2.value3'
let value = 'Hello'

let obj = createObj(keys, value)

嗳卜坏 2024-11-04 04:32:23

接下来是 TimDog 的回答,这只会为最后一个元素增加价值

function createNestedObject2(base, names, value = null) {
    for (let i = 0; i < names.length; i++) {
        base = base[names[i]] = i == names.length - 1 ? value : {};
    }
}

var obj = {};
createNestedObject2(obj, ["a", "b", "c"], "val");
console.log(JSON.stringify(obj, null, 4));

结果:

{
"a": {
    "b": {
        "c": "val"
    }
}

Followed by TimDog's answer this adds value to last element only

function createNestedObject2(base, names, value = null) {
    for (let i = 0; i < names.length; i++) {
        base = base[names[i]] = i == names.length - 1 ? value : {};
    }
}

var obj = {};
createNestedObject2(obj, ["a", "b", "c"], "val");
console.log(JSON.stringify(obj, null, 4));

Result:

{
"a": {
    "b": {
        "c": "val"
    }
}
誰認得朕 2024-11-04 04:32:23

Eval 可能有点矫枉过正,但结果很容易可视化,没有嵌套循环或递归。

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

基本上,您正在逐步编写字符串 "obj['one']= {}", "obj['one']['two']"= {} 并对其进行评估;

Eval is probably overkill but the result is simple to visualize, with no nested loops or recursion.

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

Basically you are progressively writing a string "obj['one']= {}", "obj['one']['two']"= {} and evaling it;

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文