我有一堆对象属性以点分隔的字符串形式出现,例如"availability_meta.supplier.price"
,我需要为record['availability_meta']['supplier']['price']
指定相应的值,依此类推。
并非所有东西都是3级深度:许多只有1级深度,而且许多深度超过3级。
有没有一种很好的方法在Javascript中以编程方式分配?例如,我需要:
["foo.bar.baz", 1] // --> record.foo.bar.baz = 1
["qux.qaz", "abc"] // --> record.qux.qaz = "abc"
["foshizzle", 200] // --> record.foshizzle = 200
我想我可以一起破解某些东西,但我没有任何好的算法,所以会很感激建议。我正在使用lodash,如果它有用,并向其他可以快速完成此工作的库开放。
编辑这是在后端并且不经常运行,所以没有必要优化大小,速度等。事实上,代码可读性对于未来的开发者来说将是一个加分。
编辑2 这与引用的副本不同。也就是说,我需要能够为同一个对象多次执行此分配,并且“重复”答案将每次只覆盖子键。请重新开启!
答案 0 :(得分:8)
让你入门的东西:
function assignProperty(obj, path, value) {
var props = path.split(".")
, i = 0
, prop;
for(; i < props.length - 1; i++) {
prop = props[i];
obj = obj[prop];
}
obj[props[i]] = value;
}
假设:
var arr = ["foo.bar.baz", 1];
您可以使用以下方式调用它:
assignProperty(record, arr[0], arr[1]);
答案 1 :(得分:8)
你在问题中提到了lodash,所以我想我应该添加他们的简单对象set()
和get()
函数。做一些像:
_.set(record, 'availability_meta.supplier.price', 99);
您可以在此处详细了解:https://lodash.com/docs#set
这些函数也可以让你做更复杂的事情,比如指定数组索引等:)
答案 2 :(得分:3)
这个怎么样?
function convertDotPathToNestedObject(path, value) {
const [last, ...paths] = path.split('.').reverse();
return paths.reduce((acc, el) => ({ [el]: acc }), { [last]: value });
}
convertDotPathToNestedObject('foo.bar.x', 'FooBar')
// { foo: { bar: { x: 'FooBar' } } }
答案 3 :(得分:2)
只做
record['foo.bar.baz'] = 99;
但这怎么会有用?对于使用Object.observe
的V8环境(Chrome或节点和声)的冒险者而言,它是严格意义上的。我们观察对象并捕获新属性的添加。当添加“属性”foo.bar.baz
时(通过赋值),我们检测到这是一个虚线属性,并将其转换为record['foo']['bar.baz']
的赋值(如果不是record['foo']
则创建record['foo']['bar']['baz']
(),然后转换为function enable_dot_assignments(changes) {
// Iterate over changes
changes.forEach(function(change) {
// Deconstruct change record.
var object = change.object;
var type = change.type;
var name = change.name;
// Handle only 'add' type changes
if (type !== 'add') return;
// Break the property into segments, and get first one.
var segments = name.split('.');
var first_segment = segments.shift();
// Skip non-dotted property.
if (!segments.length) return;
// If the property doesn't exist, create it as object.
if (!(first_segment in object)) object[first_segment] = {};
var subobject = object[first_segment];
// Ensure subobject also enables dot assignments.
Object.observe(subobject, enable_dot_assignments);
// Set value on subobject using remainder of dot path.
subobject[segments.join('.')] = object[name];
// Make subobject assignments synchronous.
Object.deliverChangeRecords(enable_dot_assignments);
// We don't need the 'a.b' property on the object.
delete object[name];
});
}
的赋值。它是这样的:
Object.observe(record, enable_dot_assignments);
record['foo.bar.baz'] = 99;
现在你可以做到
Object.deliverChangeRecords
请注意,此类作业将异步,这可能适用于您,也可能不适合您。要解决此问题,请在分配后立即致电function dot_assignment(object, path, value) {
Object.observe(object, enable_dot_assignments);
object[path] = value;
Object.deliverChangeRecords(enable_dot_assignments);
}
dot_assignment(record, 'foo.bar.baz', 99);
。或者,尽管不像语法上那样令人愉悦,但您可以编写辅助函数,同时设置观察者:
{{1}}
答案 4 :(得分:1)
也许就像这个例子。如果没有提供对象,它将扩展提供的对象或创建一个对象。如果您提供对象中已存在的键,则它本质上是破坏性的,但如果不是您想要的,则可以更改它。使用ECMA5。
/*global console */
/*members split, pop, reduce, trim, forEach, log, stringify */
(function () {
'use strict';
function isObject(arg) {
return arg && typeof arg === 'object';
}
function convertExtend(arr, obj) {
if (!isObject(obj)) {
obj = {};
}
var str = arr[0],
last = obj,
props,
valProp;
if (typeof str === 'string') {
props = str.split('.');
valProp = props.pop();
props.reduce(function (nest, prop) {
prop = prop.trim();
last = nest[prop];
if (!isObject(last)) {
nest[prop] = last = {};
}
return last;
}, obj);
last[valProp] = arr[1];
}
return obj;
}
var x = ['fum'],
y = [
['foo.bar.baz', 1],
['foo.bar.fum', new Date()],
['qux.qaz', 'abc'],
['foshizzle', 200]
],
z = ['qux.qux', null],
record = convertExtend(x);
y.forEach(function (yi) {
convertExtend(yi, record);
});
convertExtend(z, record);
document.body.textContent = JSON.stringify(record, function (key, value, Undefined) {
/*jslint unparam:true */
/*jshint unused:false */
if (value === Undefined) {
value = String(value);
}
return value;
});
}());
&#13;