Create an object out of dot notation

前端 未结 7 1164
小鲜肉
小鲜肉 2021-01-06 01:30

This is a reverse question to this question.

Given an object x={a:1,b:2} and a string c.d=3, modify object x to the following:



        
相关标签:
7条回答
  • 2021-01-06 01:49

    What about this :

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

    Example with an input object using (could be some form values extracted with $.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);
    })
    

    Result

    {
        "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"
    }
    
    0 讨论(0)
  • 2021-01-06 01:57

    How about this?

    It'll create or copy/overwrite an existing object.

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

    NOTE: ES6 needed for arrow functions and Object.assign().

    Feel free to play around:

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

    0 讨论(0)
  • 2021-01-06 01:59

    I believe dojo's setObject does what you want. If you (understandably) don't want to pull in all of dojo then I would recommend examining their (freely available) source or loading just base (only 4k) via AMD. It looks something like this :

    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
    }
    

    So in your case you would do :

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

    Warning : unless you only ever deal with trivial cases, I would urge you to still go check out the full dojo implementation, which deals with cases where no context is provided etc.

    0 讨论(0)
  • 2021-01-06 02:05

    Off the top of my head I guess you can do something like this:

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

    Demo: http://jsfiddle.net/E8dMF/1/

    0 讨论(0)
  • 2021-01-06 02:06

    You can do this with 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 } }
    
    0 讨论(0)
  • 2021-01-06 02:06

    Here's a heavily commented version that should be somewhat straightforward to understand.

    // 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
    
    0 讨论(0)
提交回复
热议问题