我有
data =
{
'first': {
'number': 1,
'text': 'Ya.'
},
'second': {
'number': 10,
'text': 'Da.'
}
};
我真的想要访问它:
number = data['first.number'];
实际上是以更灵活的方式,例如:
numberOrText = data[memberName+'.'+propertyName];
是否有可以建议的轻量级库或代码段? 这是 - https://github.com/martinvl/KVCObject - 太酷了,但有点开销。
答案 0 :(得分:11)
您可以使用 reduce function 轻松解决密钥路径,而无需使用任何库。
首先,我们创建一个名为 target 的示例对象,其中包含一些嵌套对象:
const target = {
foo: {
bar: {
example: 65
}
}
};
然后,我们定义一个包含keypath字符串的变量 keypath :我们想要访问目标对象中的 example 属性。
const keypath = 'foo.bar.example';
今天的辛勤工作开始了! Keypath被点分隔符拆分,我们获得了一个键数组。我们迭代这个数组(使用reduce函数),并且对于每次迭代,我们返回一个新对象。
const result = keypath.split('.').reduce((previous, current) => {
return previous[current];
}, target);
最后,结果变量值为65.它有效!
答案 1 :(得分:2)
基于@dandavis pretty simple suggestions,我可以将访问者设置为原型属性。
否eval
,在使用Object.prototype
枚举方面也保持Object.defineProperty
不变。
解决方案实际上是这样的:
function stringContains(string, value)
{ return string.indexOf(value) != -1; }
Object.defineProperty(Object.prototype, "setValueForKey", { value: function(value, key)
{ this[key] = value; }});
Object.defineProperty(Object.prototype, "setValueForKeyPath", { value: function(value, keyPath)
{
if (keyPath == null) return;
if (stringContains(keyPath, '.') == false) { this.setValueForKey(value, keyPath); return; }
var chain = keyPath.split('.');
var firstKey = chain.shift();
var shiftedKeyPath = chain.join('.');
this[firstKey].setValueForKeyPath(value, shiftedKeyPath);
}});
Object.defineProperty(Object.prototype, "getValueForKey", { value: function(key)
{ return this[key]; }});
Object.defineProperty(Object.prototype, "getValueForKeyPath", { value: function(keyPath)
{
if (keyPath == null) return;
if (stringContains(keyPath, '.') == false) { return this.getValueForKey(keyPath); }
var chain = keyPath.split('.');
var firstKey = chain.shift();
var shiftedKeyPath = chain.join('.');
return this[firstKey].getValueForKeyPath(shiftedKeyPath);
}});
测试很好:
data = {
'name' : 'data',
'first': {
'number': 1,
'text': 'Ya.',
'meta' : {
'lang' : 'en'
}
},
'second': {
'number': 10,
'text': 'Ba.',
'meta' : {
'lang' : 'en'
}
},
'third': {
'number': 100,
'text': 'Da.',
'meta' : {
'lang' : 'hu'
}
}
};
data.setValueForKey('chunk', 'name');
data.setValueForKeyPath('blob', 'name');
var thirdLanguage = data.getValueForKeyPath('third.meta.lang');
data.setValueForKeyPath(thirdLanguage, 'first.meta.lang');
data.setValueForKeyPath(thirdLanguage, 'second.meta.lang');
log(data);
与每个数据成员中的语言hu
的输出相同。
答案 2 :(得分:2)
如果你有所有基于点的路径(没有数组语法),你可以使用eval或简单的滑动递归函数:
var data = {
'first': {
'number': 1,
'text': 'Ya.'
},
'second': {
'number': 10,
'text': 'Da.'
}
};
// the simple but discouraged way using eval:
alert(
eval(
"data.second.text"
)
); //shows "Da."
// a non-eval looping solution take s bit more code, but can be faster to execute:
function resolve(obj, path){
var r=path.split(".");
if(path){return resolve(obj[r.shift()], r.join("."));}
return obj
}
alert(
resolve(data, "first.text")
); //shows: "Ya."
答案 3 :(得分:2)
我想你可能会喜欢underscore-keypath。
var foo = {
bar : {
name : "Cool!"
},
scores : [55, 27, 100, 33]
};
_(foo).valueForKeyPath("bar.name"); // --> "Cool!"
_(foo).setValueForKeyPath("bar.name", "BAR"); // --> sets foo.bar.name as "BAR"
_(foo).valueForKeyPath("scores.@max"); // --> 100
答案 4 :(得分:1)
答案 5 :(得分:0)
创建一个辅助函数,读取可变数量的参数或参数数组。
Object.prototype.$ = function() {
var result = this;
var list;
/*
Array .$(["first", "text"])
String .$("second.number")
String Parameters .$("first", "text")
*/
if(arguments.length == 1 && Object.prototype.toString.call(arguments[0]) === "[object Array]")
list = arguments[0];
else if(arguments.length == 1 && typeof(arguments[0]) == 'string' && arguments[0].indexOf(".") >= 0)
list = arguments[0].split(".");
else
list = arguments;
for(var i=0; i<list.length; i++)
result = result[list[i]];
return result;
}
// test it
data =
{
'first': {
'number': 1,
'text': 'Ya.'
},
'second': {
'number': 10,
'text': 'Da.'
}
};
var s = "second";
var s2 = "first.number";
console.log(data.$("first", "text"));
console.log(data.$(s, "number"));
console.log(data.$(["first", "number"]));
console.log(data.$(s2));
编辑您还可以创建辅助函数来对对象进行去标准化,但只有在对其进行非规范化后才读取值,因为编辑值会导致冲突,因为对象将具有内部对象值的副本。
示例:
data["first"]["number"] == data["first.number"];
data["first.number"] = -1;
data["first"]["number"] != data["first.number"];
取消规范化代码
function denormalize(obj, lv) {
var more = false;
for(var k in obj) {
if(k.split(".").length == lv) {
var node = obj[k]
if(node && typeof(node) == 'object') {
more = true;
for(var k2 in node) {
obj[k + "." + k2] = node[k2];
}
}
}
}
if(more)
denormalize(obj, lv + 1);
return obj;
}
// test it
data =
{
'first': {
'number': 1,
'text': 'Ya.'
},
'second': {
'number': 10,
'text': 'Da.'
},
"third": [{"number": 5, "text": "meh"},{"number": 6, "text": "beh"}]
};
denormalize(data, 1);
for(var k in data)
console.log(k + " : " + data[k]);
答案 6 :(得分:0)
ES2015可以使用解构:
data =
{
'first': {
'number': 1,
'text': 'Ya.'
},
'second': {
'number': 10,
'text': 'Da.'
}
};
const {first:{number: yourValue}} = data;
console.log(yourValue); // 1
答案 7 :(得分:0)
我对此有点迟了,但是我需要同样的东西,并且认为这是小型且实用的。 (它期望您split('.')
your.key.path
成为['your', 'key', 'path']
data =
{
'first': {
'number': 1,
'text': 'Ya.'
},
'second': {
'number': 10,
'text': 'Da.',
'array': ['a', {'b':'bar'}, 'c']
}
};
function valueAtPath(object, path) {
if (!object || path.length === 0) return object
return valueAtPath(object[path.shift()], path)
}
function setValueAtPath(object, path, value) {
if (!object || path.length === 0) return null
if (path.length === 1) object[path[0]] = value
else return setValueAtPath(object[path.shift()], path, value)
}
console.log(valueAtPath(data, ['second', 'array', 1, 'b']))
setValueAtPath(data, ['second', 'array', 1, 'b'], 'foo')
console.log(data)