使用不带 eval 的字符串键访问或创建嵌套的 JavaScript 对象

IT技术 javascript json nested eval
2021-03-19 09:02:13

我正在寻找一个很好的解决方案来通过字符串值访问一个属性,但如果该属性不存在,它应该创建它。如果根结构已经定义了结构的某些部分,则不应覆盖属性,而是合并。

例如,如果您有一个空对象test并且您想在不使用 eval 的情况下设置一个深层结构。例如

test = {}
test.foo.name = "Hallo" // <<- foo is an Object
test.foo[3] = "Test" // <<- foo should remain as Object, not as Array 
test.foo.data[3].bar = 100 // <<- should not overwrite test.foo.name

我写了一个实际有效的解决方案,但它的代码很糟糕,我猜:

也可用作 jsfiddle:https ://jsfiddle.net/gvaLzqqf/4/

Object.setValue = function(node, flatKey, value) {
    flatKey = flatKey.replace("[", ".");
    flatKey = flatKey.replace("]", "");
    var parts = flatKey.split(".")
    var oldNode = node
    parts.forEach(function(key, index) {
      if (/^\+?(0|[1-9]\d*)$/.test(key)) {
        key = key * 1
        if (index > 0) {
          var oldValue = parts[index - 1]
          if (!Array.isArray(oldNode[oldValue])) {
            oldNode[oldValue] = []
            node = oldNode[oldValue]
          }
        }
      }
      if (node[key] == undefined) {
        node[key] = {}
      }
      oldNode = node
      node = node[key]
    }); // for each
    oldNode[parts[parts.length - 1]] = value
    return oldNode[parts[parts.length - 1]]
  } // function

var test = {}
Object.setValue(test, "foo.name", "Mr. Foo")
Object.setValue(test, "foo.data[0].bar", 100)
Object.setValue(test, "and.another[2].deep", 20)

console.log("test = " + JSON.stringify(test))
console.log("test.foo.data[0].bar = " + test.foo.data[0].bar)

然而,有没有更好的方法来实现这一目标?

2个回答

您可以通过行走给定的对象来分割路径并减少路径。如果不存在 Object,则使用名称或数组创建一个新属性。稍后赋值。

function setValue(object, path, value) {
    var way = path.replace(/\[/g, '.').replace(/\]/g, '').split('.'),
        last = way.pop();

    way.reduce(function (o, k, i, kk) {
        return o[k] = o[k] || (isFinite(i + 1 in kk ? kk[i + 1] : last) ? [] : {});
    }, object)[last] = value;
}

var test = {};
setValue(test, "foo.name", "Mr. Foo");
setValue(test, "foo.data[0].bar", 100);
setValue(test, "and.another[2].deep", 20);
console.log(test);

正是我正在寻找的!谢谢!这个解决方案既优雅又漂亮。但是仍然缺少一件事,数组将作为对象处理。如果您运行您的解决方案,您将看到数组用作字符串键而不是整数键。因此结构最后是错误的:而不是"key" : [ {}, {},... ]你有"key" : {"0": {}, "1":{}... ]
2021-05-07 09:02:13
哇!当然你会有undefined,因为它是一个数组。但这正是人们所期望的。非常感谢,非常好,又好又轻的解决方案。
2021-05-14 09:02:13
我为数组添加了想要的功能,但你现在得到了"another": [ undefined, undefined, { "deep": 20 }]
2021-05-17 09:02:13

在这种情况下,我不会重新发明轮子,而是使用lodash特别是set()函数。根据他们的例子:

var object = { 'a': [{ 'b': { 'c': 3 } }] };

_.set(object, 'a[0].b.c', 4);
console.log(object.a[0].b.c);
// => 4

_.set(object, ['x', '0', 'y', 'z'], 5);
console.log(object.x[0].y.z);
// => 5
您的问题明确表示,I am looking for a nice solution to access a property by string value& How ever, is there any better way to achieve this?我会争辩说我已经完全回答了你的问题,而且实际上是正确的答案。使用 lodash 是解决问题更好方法,因为它完全满足您的需求,并且不会导致错误等。您的问题没有指定不使用已经过验证的库。
2021-05-01 09:02:13
很抱歉,我只能将一种解决方案标记为“已接受”。Ninas 解决方案更符合我的要求,从可用性的角度来看它和你的一样好,但它不需要添加完整的第三方库。我不得不承认我的问题不够清楚,如果有一种优雅的方式来实现这一点,我应该补充说我更喜欢非第三方解决方案。但是,Ninas 解决方案正好解决了我的问题,并没有做更多的事情。这就是为什么我更愿意给她点赞。
2021-05-07 09:02:13
尽管此解决方案没有回答问题,但它仍然有效。无论如何,我决定接受该解决方案,将 lodash 包含在我的项目中并不重要。所以感谢您的解决方案。Nina Scholz 的解决方案正是我一直在寻找的。
2021-05-17 09:02:13