Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/476.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 动态设置嵌套对象的属性_Javascript_Ecmascript 5 - Fatal编程技术网

Javascript 动态设置嵌套对象的属性

Javascript 动态设置嵌套对象的属性,javascript,ecmascript-5,Javascript,Ecmascript 5,我有一个对象,它可以有任意深度,也可以有任何现有属性。 例如: var obj={ db:{ mongodb:{ 主机:“localhost” } } }; 在此基础上,我希望设置(或覆盖)如下属性: set('db.mongodb.user','root'); //或: 集合('foo.bar','baz'); 其中属性字符串可以有任何深度,值可以是任何类型/事物。 如果属性键已经存在,则不需要合并作为值的对象和数组 上一个示例将生成以下对象: var obj={ db:{ mongodb

我有一个对象,它可以有任意深度,也可以有任何现有属性。 例如:

var obj={
db:{
mongodb:{
主机:“localhost”
}
}
};
在此基础上,我希望设置(或覆盖)如下属性:

set('db.mongodb.user','root');
//或:
集合('foo.bar','baz');
其中属性字符串可以有任何深度,值可以是任何类型/事物。
如果属性键已经存在,则不需要合并作为值的对象和数组

上一个示例将生成以下对象:

var obj={
db:{
mongodb:{
主机:“localhost”,
用户:'root'
}
},
傅:{
酒吧:巴兹
}
};

如何实现这样一个函数?

此函数使用您指定的参数,应该在
obj
容器中添加/更新数据。注意,您需要跟踪
obj
schema中的哪些元素是容器,哪些是值(字符串、int等),否则您将开始抛出异常

obj = {};  // global object

function set(path, value) {
    var schema = obj;  // a moving reference to internal objects within obj
    var pList = path.split('.');
    var len = pList.length;
    for(var i = 0; i < len-1; i++) {
        var elem = pList[i];
        if( !schema[elem] ) schema[elem] = {}
        schema = schema[elem];
    }

    schema[pList[len-1]] = value;
}

set('mongo.db.user', 'root');
obj={};//全局对象
函数集(路径、值){
var schema=obj;//对obj内部对象的移动引用
var pList=path.split('.');
var len=最长长度;
对于(变量i=0;i
如果您只需要更改更深的嵌套对象,那么另一种方法可以是引用该对象。由于JS对象由它们的引用处理,因此可以创建对具有字符串键访问权限的对象的引用

例如:

// The object we want to modify:
var obj = {
    db: {
        mongodb: {
            host: 'localhost',
            user: 'root'
        }
    },
    foo: {
        bar: baz
    }
};

var key1 = 'mongodb';
var key2 = 'host';

var myRef = obj.db[key1]; //this creates a reference to obj.db['mongodb']

myRef[key2] = 'my new string';

// The object now looks like:
var obj = {
    db: {
        mongodb: {
            host: 'my new string',
            user: 'root'
        }
    },
    foo: {
        bar: baz
    }
};
const obj = {
  boats: {
    m1: 'lady blue'
  }
};
leaf(obj, 'boats.m1', 'lady blue II');
leaf(obj, 'boats.m2', 'lady bird');
console.log(obj); // { boats: { m1: 'lady blue II', m2: 'lady bird' } }
var mod = require("nested-property");
var obj = {
  a: {
    b: {
      c: {
        d: 5
      }
    }
  }
};
console.log(mod.get(obj, "a.b.c.d"));
mod.set(obj, "a.b.c.d", 6);
console.log(mod.get(obj, "a.b.c.d"));

另一种方法是使用递归挖掘对象:

(函数(根){
函数NestedSetterAndGetter(){
函数setValueByArray(对象、部件、值){
如果(!零件){
抛出“未传入部件数组”;
}
如果(parts.length==0){
抛出“零件的长度不应为0”;
}
如果(parts.length==1){
obj[零件[0]]=值;
}否则{
var next=parts.shift();
如果(!obj[next]){
obj[next]={};
}
setValueByArray(对象[next],部件,值);
}
}
函数getValueByArray(对象、部件、值){
如果(!零件){
返回null;
}
如果(parts.length==1){
返回obj[零件[0]];
}否则{
var next=parts.shift();
如果(!obj[next]){
返回null;
}
返回getValueByArray(obj[next],零件,值);
}
}
this.set=函数(对象、路径、值){
setValueByArray(对象,路径分割('.'),值);
};
this.get=函数(对象,路径){
返回getValueByArray(obj,path.split('.');
};
}
root.NestedSetterAndGetter=NestedSetterAndGetter;
})(本条);
var setter=newthis.NestedSetterAndGetter();
var o={};
setter.set(o,'a.b.c','apple');
console.log(o);//=>{a:{b:{c:'apple'}}}
var z={a:{b:{c:{d:'test'}};
set(z'a.b.c',{dd'zzz'});
console.log(JSON.stringify(z));//=>{“a”:{“b”:{“c”:{“dd”:“zzz”}}}
log(JSON.stringify(setter.get(z,'a.b.c'));//=>{“dd”:“zzz”}
log(JSON.stringify(setter.get(z,'a.b'));//=>{“c”:{“dd”:“zzz”}

Lodash有一个名为的方法,可以完全满足您的需要

此方法接收以下参数:

  • 要更新的对象
  • 要更新的属性的路径(该属性可以深度嵌套)
  • 返回要更新的值的函数(将原始值作为参数给定)
  • 在您的示例中,它将如下所示:

    _.update(obj, 'db.mongodb.user', function(originalValue) {
      return 'root'
    })
    
    洛达斯有一种方法


    有点晚了,但这里有一个非库的更简单的答案:

    /**
     * Dynamically sets a deeply nested value in an object.
     * Optionally "bores" a path to it if its undefined.
     * @function
     * @param {!object} obj  - The object which contains the value you want to change/set.
     * @param {!array} path  - The array representation of path to the value you want to change/set.
     * @param {!mixed} value - The value you want to set it to.
     * @param {boolean} setrecursively - If true, will set value of non-existing path as well.
     */
    function setDeep(obj, path, value, setrecursively = false) {
        path.reduce((a, b, level) => {
            if (setrecursively && typeof a[b] === "undefined" && level !== path.length){
                a[b] = {};
                return a[b];
            }
    
            if (level === path.length){
                a[b] = value;
                return value;
            } 
            return a[b];
        }, obj);
    }
    
    我制作的这个函数可以完全满足您的需要,而且还可以做得更多

    假设我们要更改此对象中深度嵌套的目标值:

    let myObj = {
        level1: {
            level2: {
               target: 1
           }
        }
    }
    
    我们可以这样调用函数:

    setDeep(myObj, ["level1", "level2", "target1"], 3);
    
    将导致:

    myObj={ 第1级:{ 第2级:{ 目标:3 } } }

    如果对象不存在,则将set recursive标志设置为true将设置对象

    setDeep(myObj, ["new", "path", "target"], 3, true);
    
    这将导致:

    obj = myObj = {
        new: {
             path: {
                 target: 3
             }
        },
        level1: {
            level2: {
               target: 3
           }
        }
    }
    

    灵感来自@bpmason1的答案:

    function leaf(obj, path, value) {
      const pList = path.split('.');
      const key = pList.pop();
      const pointer = pList.reduce((accumulator, currentValue) => {
        if (accumulator[currentValue] === undefined) accumulator[currentValue] = {};
        return accumulator[currentValue];
      }, obj);
      pointer[key] = value;
      return obj;
    }
    
    例如:

    // The object we want to modify:
    var obj = {
        db: {
            mongodb: {
                host: 'localhost',
                user: 'root'
            }
        },
        foo: {
            bar: baz
        }
    };
    
    var key1 = 'mongodb';
    var key2 = 'host';
    
    var myRef = obj.db[key1]; //this creates a reference to obj.db['mongodb']
    
    myRef[key2] = 'my new string';
    
    // The object now looks like:
    var obj = {
        db: {
            mongodb: {
                host: 'my new string',
                user: 'root'
            }
        },
        foo: {
            bar: baz
        }
    };
    
    const obj = {
      boats: {
        m1: 'lady blue'
      }
    };
    leaf(obj, 'boats.m1', 'lady blue II');
    leaf(obj, 'boats.m2', 'lady bird');
    console.log(obj); // { boats: { m1: 'lady blue II', m2: 'lady bird' } }
    
    var mod = require("nested-property");
    var obj = {
      a: {
        b: {
          c: {
            d: 5
          }
        }
      }
    };
    console.log(mod.get(obj, "a.b.c.d"));
    mod.set(obj, "a.b.c.d", 6);
    console.log(mod.get(obj, "a.b.c.d"));
    

    我们可以使用递归函数:

    /**
     * Sets a value of nested key string descriptor inside a Object.
     * It changes the passed object.
     * Ex:
     *    let obj = {a: {b:{c:'initial'}}}
     *    setNestedKey(obj, ['a', 'b', 'c'], 'changed-value')
     *    assert(obj === {a: {b:{c:'changed-value'}}})
     *
     * @param {[Object]} obj   Object to set the nested key
     * @param {[Array]} path  An array to describe the path(Ex: ['a', 'b', 'c'])
     * @param {[Object]} value Any value
     */
    export const setNestedKey = (obj, path, value) => {
      if (path.length === 1) {
        obj[path] = value
        return
      }
      return setNestedKey(obj[path[0]], path.slice(1), value)
    }
    

    它更简单

    ES6也有一种非常酷的方法,可以使用计算属性名和Rest参数来实现这一点


    如果
    levelThree
    是一个动态属性,即要在
    levelTwo
    中设置任何属性,可以使用
    [propertyName]:“我现在更新了!”
    其中
    propertyName
    levelTwo
    中保存属性的名称,我只是使用ES6+递归编写了一个小函数来实现这个目标

    updateObjProp = (obj, value, propPath) => {
        const [head, ...rest] = propPath.split('.');
    
        !rest.length
            ? obj[head] = value
            : this.updateObjProp(obj[head], value, rest.join('.'));
    }
    
    const user = {profile: {name: 'foo'}};
    updateObjProp(user, 'fooChanged', 'profile.name');
    
    我在react to update state上经常使用它,它对我来说效果很好。

    我创建它是为了根据正确答案通过字符串设置和获取obj值。您可以下载它或将其用作npm/纱线包

    // yarn add gist:5ceba1081bbf0162b98860b34a511a92
    // npm install gist:5ceba1081bbf0162b98860b34a511a92
    export const DeepObject = {
      set: setDeep,
      get: getDeep
    };
    
    // https://stackoverflow.com/a/6491621
    function getDeep(obj: Object, path: string) {
      path = path.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
      path = path.replace(/^\./, '');           // strip a leading dot
      const a = path.split('.');
      for (let i = 0, l = a.length; i < l; ++i) {
        const n = a[i];
        if (n in obj) {
          obj = obj[n];
        } else {
          return;
        }
      }
    
      return obj;
    }
    
    // https://stackoverflow.com/a/18937118
    function setDeep(obj: Object, path: string, value: any) {
      let schema = obj;  // a moving reference to internal objects within obj
      const pList = path.split('.');
      const len = pList.length;
      for (let i = 0; i < len - 1; i++) {
        const elem = pList[i];
        if (!schema[elem]) {
          schema[elem] = {};
        }
        schema = schema[elem];
      }
    
      schema[pList[len - 1]] = value;
    }
    
    // Usage
    // import {DeepObject} from 'somePath'
    //
    // const obj = {
    //   a: 4,
    //   b: {
    //     c: {
    //       d: 2
    //     }
    //   }
    // };
    //
    // DeepObject.set(obj, 'b.c.d', 10); // sets obj.b.c.d to 10
    // console.log(DeepObject.get(obj, 'b.c.d')); // returns 10
    
    //纱线添加要点:5ceba1081bbf0162b98860b34a511a92
    //npm安装要点:5ceba1081bbf0162b98860b34a511a92
    导出常量DeepObject={
    set:setDeep,
    获取:获取深度
    };
    // https://stackoverflow.com/a/6491621
    函数getDeep(obj:Object,path:string){
    path=path.replace(/\[(\w+)\]/g,.$1');//将索引转换为属性
    路径=路径。替换(/^\./,“”);//去掉前导点
    常数a=路径分割('.');
    for(设i=0,l=a.length;i/**
     * Associate value (v) in object/array (m) at key/index (k).
     * If m is falsy, use new object.
     * Returns the updated object/array.
     */
    function assoc(m, k, v) {
        m = (m || {});
        m[k] = v;
        return m;
    }
    
    /**
     * Associate value (v) in nested object/array (m) using sequence of keys (ks)
     * to identify the path to the nested key/index.
     * If one of the values in the nested object/array doesn't exist, it adds
     * a new object.
     */
    function assoc_in(m={}, [k, ...ks], v) {
        return ks.length ? assoc(m, k, assoc_in(m[k], ks, v)) : assoc(m, k, v);
    }
    
    /**
     * Associate value (v) in nested object/array (m) using key string notation (s)
     * (e.g. "k1.k2").
     */
    function set(m, s, v) {
        ks = s.split(".");
        return assoc_in(m, ks, v);
    }
    
    assoc_in({"a": 1}, ["a", "b"], 2) 
    
    {"a": 1}
    
    const set = (o, path, value) => {
        const props = path.split('.');
        const prop = props.shift()
        if (props.length === 0) {
            o[prop] = value
        } else {
            o[prop] = o[prop] ?? {}
            set(o[prop], props.join('.'), value)
        }
    }