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