Как установить свойство объекта (свойства объекта...), учитывая его имя строки в JavaScript?
Предположим, что нам дают только
var obj = {};
var propName = "foo.bar.foobar";
Как мы можем установить свойство obj.foo.bar.foobar
на определенное значение (например, "привет мир" )?
Поэтому я хочу достичь этого, в то время как у нас есть только имя свойства в строке:
obj.foo.bar.foobar = "hello world";
Ответы
Ответ 1
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");
Ответ 2
Поскольку на этот вопрос отвечают неправильные ответы, я просто буду ссылаться на правильный ответ из аналогичного вопроса
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');
Ответ 3
edit: Я создал тестовый файл jsPerf.com, чтобы сравнить принятый ответ с моей версией.
Оказывается, моя версия быстрее, особенно когда вы очень глубоко.
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");
Ответ 4
Все решения переопределяют любые исходные данные при настройке, поэтому я изменил их со следующим, превратил их в один объект:
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 param:
Object.prototype = { setNested = function(){ ... }, getNested = function(){ ... } }
{}.setNested('a.c','foo')
Ответ 5
Я знаю, что это старый, но я вижу только пользовательские функции в ответах.
Если вы не возражаете против использования библиотеки, посмотрите lodash _.set
и _.get
.
Ответ 6
Здесь тот, который возвращает обновленный объект
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'} }
Ответ 7
Вот простая функция, использующая ссылку.
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;
}
Ответ 8
Очень простой.
Нет рекурсий или обратных вызовов.
function setDeepVal(obj, path, val) {
var props = path.split('.');
for (var i = 0, n = props.length - 1; i < n; ++i) {
obj = obj[props[i]] = obj[props[i]] || {};
}
obj[props[i]] = val;
return obj;
}
// TEST
var obj = { hello : 'world' };
setDeepVal(obj, 'foo.bar.baz', 1);
setDeepVal(obj, 'foo.bar2.baz2', 2);
console.log(obj);
Ответ 9
Вот функция get и set, которую я просто скомпилировал из пары потоков + некоторый код.
Он также создаст ключи, которые не существуют в наборе.
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;
}
Ответ 10
Вы можете разделить путь и сделать проверку, существует ли следующий элемент. Если не назначить объект новому свойству.
Возвращает значение свойства.
В конце присвойте значение.
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);