提问人:chtenb 提问时间:12/5/2012 最后编辑:user2864740chtenb 更新时间:8/9/2022 访问量:36625
如何在JavaScript中设置对象属性(对象属性...)给定其字符串名称?
How to set object property (of object property of..) given its string name in JavaScript?
问:
假设我们只得到
var obj = {};
var propName = "foo.bar.foobar";
我们如何将属性设置为某个值(比如“hello world”)?
所以我想实现这一点,而我们只有一个字符串中的属性名称:obj.foo.bar.foobar
obj.foo.bar.foobar = "hello world";
答:
由于这个问题似乎被错误的答案所回答,我将只参考类似问题的正确答案
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;
}
}
用:
var obj = {};
setDeepValue(obj, 'Hello World', 'foo.bar.foobar');
评论
=
typeof
typeof
foo.bar.foobar
foo.bar2.foobar2
obj.foo
foo.bar2.foobar2
false
if(obj[p]!=null && typeof obj[p]=== 'object')
function assign(obj, prop, value) {
if (typeof prop === "string")
prop = prop.split(".");
if (prop.length > 1) {
var e = prop.shift();
assign(obj[e] =
Object.prototype.toString.call(obj[e]) === "[object Object]"
? obj[e]
: {},
prop,
value);
} else
obj[prop[0]] = value;
}
var obj = {},
propName = "foo.bar.foobar";
assign(obj, propName, "Value");
评论
obj[prop[0]] = value;
prop
编辑:我创建了一个 jsPerf.com 测试用例,将接受的答案与我的版本进行比较。 事实证明,我的版本更快,尤其是当你深入时。
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");
评论
这是我刚刚从几个线程+一些自定义代码编译的获取和设置函数。
它还将创建片场不存在的密钥。
function setValue(object, path, value) {
var a = path.split('.');
var o = object;
for (var i = 0; i < a.length - 1; i++) {
var n = a[i];
if (n in o) {
o = o[n];
} else {
o[n] = {};
o = o[n];
}
}
o[a[a.length - 1]] = value;
}
function getValue(object, path) {
var o = object;
path = path.replace(/\[(\w+)\]/g, '.$1');
path = path.replace(/^\./, '');
var a = path.split('.');
while (a.length) {
var n = a.shift();
if (n in o) {
o = o[n];
} else {
return;
}
}
return o;
}
评论
所有解决方案在设置时都会覆盖任何原始数据,因此我调整了以下内容,也将其制作成一个对象:
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;
}
};
也可以将函数更改为 Oject.prototype 方法,将 obj 参数更改为:
Object.prototype = { setNested = function(){ ... }, getNested = function(){ ... } }
{}.setNested('a.c','foo')
下面是一个返回更新对象的对象
function deepUpdate(value, path, tree, branch = tree) {
const last = path.length === 1;
branch[path[0]] = last ? value : branch[path[0]];
return last ? tree : deepUpdate(value, path.slice(1), tree, branch[path[0]]);
}
const path = 'cat.dog';
const updated = deepUpdate('a', path.split('.'), {cat: {dog: null}})
// => { cat: {dog: 'a'} }
这是一个使用引用执行此操作的简单函数。
function setValueByPath (obj, path, value) {
var ref = obj;
path.split('.').forEach(function (key, index, arr) {
ref = ref[key] = index === arr.length - 1 ? value : {};
});
return obj;
}
我知道这是一个旧的,但我只在答案中看到自定义函数。
如果您不介意使用库,请查看 lodash _.set
和 _.get
函数。
您可以拆分路径并检查以下元素是否存在。如果没有,则将对象分配给新属性。
然后返回属性的值。
最后,分配值。
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);
一个非常简单的问题。
此实现应该非常高性能。 它避免了递归和函数调用,同时保持了简单性。
/**
* Set the value of a deep property, creating new objects as necessary.
* @param {Object} obj The object to set the value on.
* @param {String|String[]} path The property to set.
* @param {*} value The value to set.
* @return {Object} The object at the end of the path.
* @author github.com/victornpb
* @see https://stackoverflow.com/a/46060952/938822
* @example
* setDeep(obj, 'foo.bar.baz', 'quux');
*/
function setDeep(obj, path, value) {
const props = typeof path === 'string' ? path.split('.') : path;
for (var i = 0, n = props.length - 1; i < n; ++i) {
obj = obj[props[i]] = obj[props[i]] || {};
}
obj[props[i]] = value;
return obj;
}
/*********************** EXAMPLE ***********************/
const obj = {
hello : 'world',
};
setDeep(obj, 'root', true);
setDeep(obj, 'foo.bar.baz', 1);
setDeep(obj, ['foo','quux'], '😉');
console.log(obj);
// ⬇︎ Click "Run" below to see output
评论
setDeep(obj, 'foo.bar.baz[0]', 1);
我一直在寻找一个不会覆盖现有值且易于阅读并且能够提出这个问题的答案。把它留在这里,以防它帮助其他有同样需求的人
function setValueAtObjectPath(obj, pathString, newValue) {
// create an array (pathComponents) of the period-separated path components from pathString
var pathComponents = pathString.split('.');
// create a object (tmpObj) that references the memory of obj
var tmpObj = obj;
for (var i = 0; i < pathComponents.length; i++) {
// if not on the last path component, then set the tmpObj as the value at this pathComponent
if (i !== pathComponents.length-1) {
// set tmpObj[pathComponents[i]] equal to an object of it's own value
tmpObj[pathComponents[i]] = {...tmpObj[pathComponents[i]]}
// set tmpObj to reference tmpObj[pathComponents[i]]
tmpObj = tmpObj[pathComponents[i]]
// else (IS the last path component), then set the value at this pathComponent equal to newValue
} else {
// set tmpObj[pathComponents[i]] equal to newValue
tmpObj[pathComponents[i]] = newValue
}
}
// return your object
return obj
}
与 Rbar 的答案相同,当您使用 redux reducers 时非常有用。我也使用 lodash clone 而不是 spread 运算符来支持数组:
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;
}
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;
}
};
下面是一个简单的方法,它使用一个范围,该范围递归地按路径设置正确的道具。Object
function setObjectValueByPath(pathScope, value, obj) {
const pathStrings = pathScope.split('/');
obj[pathStrings[0]] = pathStrings.length > 1 ?
setObjectValueByPath(
pathStrings.splice(1, pathStrings.length).join('/'),
value,
obj[pathStrings[0]]
) :
value;
return obj;
}
一个简单而简短的怎么样?
Object.assign(this.origin, { [propName]: value })
您可以使用:(您可以通过在浏览器控制台上复制/粘贴来测试它)reduce
const setValueOf = (obj, value, ...path) => {
path.reduce((o, level, idx) => {
if(idx === path.length -1) { o[level] = value }; // on last change the value of the prop
return o && o[level]; // return the prop
}, obj);
};
例
let objExmp = {a: 'a', b: {b1: 'b1', b2: 'b2', b3: { b3_3 : 'default_value' } }};
setValueOf(objExmp, 'new_value' , 'b', 'b3', 'b3_3');
console.log('objExmp', objExmp); // prop changed to 'new_value'
你可以用'.'拆分字符串路径,并像这样传播:
setValueOf(objExmp, 'new_value' , ...'b.b3.b3_3'.split('.'));
评论