How to set object property (of object property of..) given its string name in JavaScript?

后端 未结 14 1885
离开以前
离开以前 2020-11-22 02:20

Suppose we are only given

var obj = {};
var propName = \"foo.bar.foobar\";

How can we set the prop

相关标签:
14条回答
  • 2020-11-22 02:51

    edit: I've created a jsPerf.com testcase to compare the accepted answer with my version. Turns out that my version is faster, especially when you go very deep.

    http://jsfiddle.net/9YMm8/

    var nestedObjectAssignmentFor = function(obj, propString, value) {
        var propNames = propString.split('.'),
            propLength = propNames.length-1,
            tmpObj = obj;
    
        for (var i = 0; i <= propLength ; i++) {
            tmpObj = tmpObj[propNames[i]] = i !== propLength ?  {} : value;  
        }
        return obj;
    }
    
    var obj = nestedObjectAssignment({},"foo.bar.foobar","hello world");
    

    0 讨论(0)
  • 2020-11-22 02:52

    Same as Rbar's answers, very useful when you're working with redux reducers. I use lodash clone instead of spread operator to support arrays too:

    export function cloneAndPatch(obj, path, newValue, separator='.') {
        let stack = Array.isArray(path) ? path : path.split(separator);
        let newObj = _.clone(obj);
    
        obj = newObj;
    
        while (stack.length > 1) {
            let property = stack.shift();
            let sub = _.clone(obj[property]);
    
            obj[property] = sub;
            obj = sub;
        }
    
        obj[stack.shift()] = newValue;
    
        return newObj;
    }
    
    0 讨论(0)
  • 2020-11-22 02:53

    Since this question appears to be answered by incorrect answers, I'll just refer to the correct answer from a similar question

    function setDeepValue(obj, value, path) {
        if (typeof path === "string") {
            var path = path.split('.');
        }
    
        if(path.length > 1){
            var p=path.shift();
            if(obj[p]==null || typeof obj[p]!== 'object'){
                 obj[p] = {};
            }
            setDeepValue(obj[p], value, path);
        }else{
            obj[path[0]] = value;
        }
    }
    

    Use:

    var obj = {};
    setDeepValue(obj, 'Hello World', 'foo.bar.foobar');
    
    0 讨论(0)
  • 2020-11-22 02:53

    You could split the path and make a check if the following element exist. If not assign an object to the new property.

    Return then the value of the property.

    At the end assign the value.

    function setValue(object, path, value) {
        var fullPath = path.split('.'),
            way = fullPath.slice(),
            last = way.pop();
    
        way.reduce(function (r, a) {
            return r[a] = r[a] || {};
        }, object)[last] = value;
    }
    
    var object = {},
        propName = 'foo.bar.foobar',
        value = 'hello world';
    
    setValue(object, propName, value);
    console.log(object);

    0 讨论(0)
  • 2020-11-22 02:56

    All solutions overid any of the original data when setting so I have tweaked with the following, made it into a single object too:

     var obj = {}
     nestObject.set(obj, "a.b", "foo"); 
     nestObject.get(obj, "a.b"); // returns foo     
    
     var nestedObject = {
         set: function(obj, propString, value) {
             var propNames = propString.split('.'),
                 propLength = propNames.length-1,
                 tmpObj = obj;
             for (var i = 0; i <= propLength ; i++) {
                 if (i === propLength){
                     if(tmpObj[propNames[i]]){
                         tmpObj[propNames[i]] = value;
                     }else{
                         tmpObj[propNames[i]] = value;
                     }
                 }else{
                     if(tmpObj[propNames[i]]){
                         tmpObj = tmpObj[propNames[i]];
                     }else{
                         tmpObj = tmpObj[propNames[i]] = {};
                     }
                 }
             }
             return obj;
         },
         get: function(obj, propString){
             var propNames = propString.split('.'),
                 propLength = propNames.length-1,
                 tmpObj = obj;
             for (var i = 0; i <= propLength ; i++) {
                 if(tmpObj[propNames[i]]){
                     tmpObj = tmpObj[propNames[i]];
                 }else{
                     break;
                 }
             }
             return tmpObj;
         }
     };
    

    Can also change functions to be an Oject.prototype method changing obj param to this:

    Object.prototype = { setNested = function(){ ... }, getNested = function(){ ... } } 
    
    {}.setNested('a.c','foo') 
    
    0 讨论(0)
  • 2020-11-22 02:57
    Object.getPath = function(o, s) {
        s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
        s = s.replace(/^\./, '');           // strip a leading dot
        var a = s.split('.');
        for (var i = 0, n = a.length; i < n; ++i) {
            var k = a[i];
            if (k in o) {
                o = o[k];
            } else {
                return;
            }
        }
        return o;
    };
    
    Object.setPath = function(o, p, v) {
        var a = p.split('.');
        var o = o;
        for (var i = 0; i < a.length - 1; i++) {
            if (a[i].indexOf('[') === -1) {
                var n = a[i];
                if (n in o) {
                    o = o[n];
                } else {
                    o[n] = {};
                    o = o[n];
                }
            } else {
                // Not totaly optimised
                var ix = a[i].match(/\[.*?\]/g)[0];
                var n = a[i].replace(ix, '');
                o = o[n][ix.substr(1,ix.length-2)]
            }
        }
    
        if (a[a.length - 1].indexOf('[') === -1) {
            o[a[a.length - 1]] = v;
        } else {
            var ix = a[a.length - 1].match(/\[.*?\]/g)[0];
            var n = a[a.length - 1].replace(ix, '');
            o[n][ix.substr(1,ix.length-2)] = v;
        }
    };
    
    0 讨论(0)
提交回复
热议问题