【问题标题】:Create an object out of dot notation用点表示法创建对象
【发布时间】:2017-12-31 11:20:28
【问题描述】:

这是this question的反向问题。

给定一个对象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 必须以某种方式解析并添加到 config.set 函数内的内部对象 x 中,因此 x 看起来像:

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

【问题讨论】:

  • 如果 C 是一个字符串,那么您将不得不尝试手动将其解析为一个对象。我几乎可以保证在您创建字符串 C 的地方这样做会更容易,而且您不必进行任何手动解析。请显示您的这部分代码,以便我进一步提供帮助
  • 字符串是否保证为dot.separated.string=value,值有什么限制?例如是 JSON 类型吗?
  • 字符串保证为dot.seperated,并且对值没有约束。

标签: javascript json


【解决方案1】:

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

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/

【讨论】:

  • 当然由提问者决定,但我建议您更新,以免破坏已经存在的属性。例如,如果 x={a:{b:3}} 并且我执行 addValueToObj(x, "a.c=4") 那么 b 消失。
【解决方案2】:

您可以通过lodash.set() 做到这一点

> l=require('lodash') > x={a:1,b:2}; { a: 1, b: 2 } > l.set(x, 'c.d', 3) { a: 1, b: 2, c: { d: 3 } }

【讨论】:

    【解决方案3】:

    我相信 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 实现,它处理没有提供上下文等的案例。

    【讨论】:

      【解决方案4】:

      这是一个注释很多的版本,应该有点简单易懂。

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

      【讨论】:

        【解决方案5】:

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

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

        // @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"
              }
            }
          }
        */
        

        【讨论】:

          【解决方案6】:

          这个怎么样?

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

          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() 需要 ES6。

          随意玩耍:

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

          【讨论】:

            【解决方案7】:

            这个呢:

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2013-07-03
              • 2012-05-14
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多