6

这是这个问题的反向问题

给定一个对象x={a:1,b:2}和一个字符串c.d=3,将对象 x 修改为以下内容:

{
  a:1,
  b:2,
  c:{
    d:3
  }
}

我正在寻找一种不使用eval. 用例如下:

x作为一个配置对象,我们称之为: config.set("music.shuffle",true)

现在,music.shuffle必须以某种方式解析并添加到xconfig.set 函数内的内部对象中,这样 x 看起来像:

x={a:1,b:2,music:{shuffle:true}}
4

7 回答 7

11

我想你可以做这样的事情:

function addValueToObj(obj, newProp) {
    newProp = newProp.split("=");       // separate the "path" from the "value"

    var path = newProp[0].split("."),     // separate each step in the "path"
        val = newProp.slice(1).join("="); // allow for "=" in "value"

    for (var i = 0, tmp = obj; i < path.length - 1; i++) {
       tmp = tmp[path[i]] = {};     // loop through each part of the path adding to obj
    }
    tmp[path[i]] = val;             // at the end of the chain add the value in
}

var x = {a:1, b:2};
addValueToObj(x, "c.d=3");
// x is now {"a":1,"b":2,"c":{"d":"3"}}
addValueToObj(x, "e.f.g.h=9=9");
// x is now {"a":1,"b":2,"c":{"d":"3"},"e":{"f":{"g":{"h":"9=9"}}}}

演示:http: //jsfiddle.net/E8dMF/1/

于 2013-02-15T09:11:39.287 回答
6

你可以这样做lodash.set()

> l=require('lodash')
> x={a:1,b:2};
{ a: 1, b: 2 }
> l.set(x, 'cd', 3)
{ a: 1, b: 2, c: { d: 3 } }
于 2016-07-06T23:09:00.717 回答
4

我相信 dojo 的 setObject 可以满足您的要求。如果您(可以理解)不想引入所有的 dojo,那么我建议您检查他们的(免费提供的)源或通过 AMD 仅加载基本(仅 4k)。它看起来像这样:

function setObject(name, value, context) {
    var parts=name.split("."), 
    p=parts.pop();
    for(var i=0, j; context && (j=parts[i]); i++){
        context = (j in context ? context[j] : context[j]={});
    }
    return context && p ? (context[p]=value) : undefined; // Object
}

所以在你的情况下,你会这样做:

x={a:1,b:2};
setObject("c.d", 3, x);

警告:除非您只处理琐碎的案例,否则我会敦促您继续检查完整的 dojo 实现,它处理没有提供上下文等的情况。

于 2013-02-15T09:13:31.087 回答
2

这是一个评论较多的版本,应该很容易理解。

// stores the configured data
configStore = {};

config = {
  set: function(keyValueString) {

    // Split the string by the =
    var pair = keyValueString.split('=');

    // left of the = is the key path
    var keyPath = pair[0];

    // right of the = is the value to set
    var value = pair[1];

    // split keyPath into an array of keys
    var keys = keyPath.split('.');
    var key; // used in loop

    // the current level of object we are drilling into.
    // Starts as the main root config object.
    var currentObj = configStore;

    // Loop through all keys in the key path, except the last one (note the -1).
    // This creates the object structure implied by the key path.
    // We want to do something different on the last iteration.
    for (var i=0; i < keys.length-1; i++) {

      // Get the current key we are looping
      key = keys[i];

      // If the requested level on the current object doesn't exist,
      // make a blank object.
      if (typeof currentObj[key] === 'undefined') {
        currentObj[key] = {};
      }

      // Set the current object to the next level of the keypath,
      // allowing us to drill in.
      currentObj = currentObj[key];
    }

    // Our loop doesn't handle the last key, because that's when we
    // want to set the actual value. So find the last key in the path.
    var lastKey = keys[keys.length-1]

    // Set the property of the deepest object to the value.
    currentObj[lastKey] = value;
  }
};

// Do it.
config.set('omg.wtf.bbq=123')

// Check it.
alert(configStore.omg.wtf.bbq); // 123
于 2013-02-15T09:28:32.297 回答
2

今天不得不做类似的事情,这是另一个解决方案。绝对可以使用一些清理,但它的伎俩。如果输入有效,这将扩展现有对象并且不会擦除任何数据。

没有验证,所以如果你传递了错误的数据,你肯定可以覆盖密钥。

// @param object orig    the object to extend
// @param array keyParts the.key.path split by "." (expects an array, presplit)
// @param mixed value    the value to assign
// @param object scoped  used by the recursion, ignore or pass null
function unflatten(orig, keyParts, value, scoped) {
    if (!scoped) {
        scoped = orig;
    }

    var nextKey = keyParts.shift();

    if (keyParts.length === 0) {
        scoped[nextKey] = value;
        return orig;
    }

    if (!scoped[nextKey]) {
        scoped[nextKey] = {};
    }

    scoped = scoped[nextKey];
    return unflatten(orig, keyParts, value, scoped);
}

函数原型可以改进,但可以满足我的需要。通过以下方式调用它:

var orig = { foo: 'hello world', bar: { baz: 'goodbye world' } };

// lets add the key "bar.cat.aww" with value "meow"
unflatten(orig, "bar.cat.aww".split("."), "meow");
/* 
  orig is { 
    foo: "hello world", 
    bar: { 
      baz: "goodbye world", 
      cat: { 
        aww: "meow" 
      } 
    } 
  }

*/

// we can keep calling it to add more keys
unflatten(orig, "some.nested.key".split("."), "weeee");

/* 
  orig is { 
    foo: "hello world", 
    bar: { 
      baz: "goodbye world", 
      cat: { 
        aww: "meow" 
      } 
    },
    some: {
      nested: {
        key: "weeee"
      }
    }
  }
*/
于 2015-11-13T01:55:18.200 回答
1

这个怎么样?

它将创建或复制/覆盖现有对象。

function expando(obj, base) {
    return Object.keys(obj)
      .reduce((clone, key) => {
        key.split('.').reduce((innerObj, innerKey, i, arr) => 
          innerObj[innerKey] = (i+1 === arr.length) ? obj[key] : innerObj[innerKey] || {}, clone)
        return clone;
    }, Object.assign({}, base));
}

console.log(expando({'a.b': 1})) // { a: { b : 1 }}
console.log(expando({'b.c': 2}, { a: 1 })) // { a: 1, b: { c: 2 }}
console.log(expando({'b.c.d': 2, 'e.f': 3}, { a: 1 })) // { a: 1, b: { c: { d: 2 } }, e: { f: 3}}

注意:箭头函数和Object.assign().

随意玩:

https://jsbin.com/setazahuce/1/edit?js,console

于 2017-05-17T02:52:56.080 回答
0

那这个呢 :

function setObj (str, value, obj) {
    var ref = obj, keys = str.split('.');
    while (keys.length) {
        var currentKey = keys.shift();
        ref[currentKey] = keys.length ? (ref[currentKey]  ? ref[currentKey] : {}) : value;
        ref = ref[currentKey];
    }
}

使用输入对象的示例(可能是使用 $.serializeArray 提取的一些表单值)

var serializedInputs = [
    {name: 'fruits[1][name]', value: 'Banana'},
    {name: 'fruits[1][id]', value: '1'},
    {name: 'fruits[2][name]', value: 'Strawberry'},
    {name: 'fruits[2][id]', value: '2'},
    {name: 'fruits[3][name]', value: 'Raspberry'},
    {name: 'fruits[3][id]', value: '3'},
    {name: 'fruits[4][name]', value: 'Kiwi'},
    {name: 'fruits[4][id]', value: '4'},
    {name: 'fruits[5][name]', value: 'Mango'},
    {name: 'fruits[5][id]', value: '5'},
    {name: 'selected_fruit_id', value: '1'},
]
// This variable holds the result
var obj = {}
serializedInputs.forEach(function(item) {
    // Turning square brackets into dot notation
    setObj(item.name.replace(/\]/g, '').replace(/\[/g, '.'), item.value, obj);
})

结果

{
    "fruits": {
        "1": {
            "name": "Banana",
            "id": "1"
        },
        "2": {
            "name": "Strawberry",
            "id": "2"
        },
        "3": {
            "name": "Raspberry",
            "id": "3"
        },
        "4": {
            "name": "Kiwi",
            "id": "4"
        },
        "5": {
            "name": "Mango",
            "id": "5"
        }
    },
    "selected_fruit_id": "1"
}
于 2017-12-31T03:20:28.010 回答