通过字符串路径访问嵌套的 JavaScript 对象和数组
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6491463/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Accessing nested JavaScript objects and arays by string path
提问by Komaruloh
I have a data structure like this :
我有这样的数据结构:
var someObject = {
'part1' : {
'name': 'Part 1',
'size': '20',
'qty' : '50'
},
'part2' : {
'name': 'Part 2',
'size': '15',
'qty' : '60'
},
'part3' : [
{
'name': 'Part 3A',
'size': '10',
'qty' : '20'
}, {
'name': 'Part 3B',
'size': '5',
'qty' : '20'
}, {
'name': 'Part 3C',
'size': '7.5',
'qty' : '20'
}
]
};
And I would like to access the data using these variable :
我想使用这些变量访问数据:
var part1name = "part1.name";
var part2quantity = "part2.qty";
var part3name1 = "part3[0].name";
part1name should be filled with someObject.part1.name
's value, which is "Part 1". Same thing with part2quantity which filled with 60.
part1name 应填写someObject.part1.name
的值,即“第 1 部分”。与填充 60 的 part2quantity 相同。
Is there anyway to achieve this with either pure javascript or JQuery?
有没有办法用纯 javascript 或 JQuery 来实现这一点?
回答by Alnitak
I just made this based on some similar code I already had, it appears to work:
我只是根据我已经拥有的一些类似代码制作了这个,它似乎有效:
Object.byString = function(o, s) {
s = s.replace(/\[(\w+)\]/g, '.'); // 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;
}
Usage::
用法::
Object.byString(someObj, 'part3[0].name');
See a working demo at http://jsfiddle.net/alnitak/hEsys/
在http://jsfiddle.net/alnitak/hEsys/查看工作演示
EDITsome have noticed that this code will throw an error if passed a string where the left-most indexes don't correspond to a correctly nested entry within the object. This is a valid concern, but IMHO best addressed with a try / catch
block when calling, rather than having this function silently return undefined
for an invalid index.
编辑有些人已经注意到,如果传递一个字符串,其中最左边的索引不对应于对象中正确嵌套的条目,则此代码将引发错误。这是一个有效的问题,但恕我直言,最好try / catch
在调用时使用块解决,而不是让此函数静默返回undefined
无效索引。
回答by speigg
This is the solution I use:
这是我使用的解决方案:
function resolve(path, obj=self, separator='.') {
var properties = Array.isArray(path) ? path : path.split(separator)
return properties.reduce((prev, curr) => prev && prev[curr], obj)
}
Example usage:
用法示例:
// accessing property path on global scope
resolve("document.body.style.width")
// or
resolve("style.width", document.body)
// accessing array indexes
// (someObject has been defined in the question)
resolve("part3.0.size", someObject) // returns '10'
// accessing non-existent properties
// returns undefined when intermediate properties are not defined:
resolve('properties.that.do.not.exist', {hello:'world'})
// accessing properties with unusual keys by changing the separator
var obj = { object: { 'a.property.name.with.periods': 42 } }
resolve('object->a.property.name.with.periods', obj, '->') // returns 42
// accessing properties with unusual keys by passing a property name array
resolve(['object', 'a.property.name.with.periods'], obj) // returns 42
Limitations:
限制:
- Can't use brackets (
[]
) for array indices—though specifying array indices between the separator token (e.g.,.
) works fine as shown above.
- 不能
[]
对数组索引使用方括号 ( ) — 尽管在分隔符标记之间指定数组索引(例如,.
)可以正常工作,如上所示。
回答by Ian Walker-Sperber
This is now supported by lodash using _.get(obj, property)
. See https://lodash.com/docs#get
现在 lodash 使用_.get(obj, property)
. 见https://lodash.com/docs#get
Example from the docs:
文档中的示例:
var object = { 'a': [{ 'b': { 'c': 3 } }] };
_.get(object, 'a[0].b.c');
// → 3
_.get(object, ['a', '0', 'b', 'c']);
// → 3
_.get(object, 'a.b.c', 'default');
// → 'default'
回答by Adriano Spadoni
ES6: Only one line in Vanila JS (it return null if don't find instead of giving error):
ES6:Vanila JS 中只有一行(如果没有找到则返回 null 而不是给出错误):
'path.string'.split('.').reduce((p,c)=>p&&p[c]||null, MyOBJ)
Or example:
或者例如:
'a.b.c'.split('.').reduce((p,c)=>p&&p[c]||null, {a:{b:{c:1}}})
For a ready to use function that also recognizes false, 0 and negative number and accept default values as parameter:
对于一个也可以识别假、0 和负数并接受默认值作为参数的即用型函数:
const resolvePath = (object, path, defaultValue) => path
.split('.')
.reduce((o, p) => o ? o[p] : defaultValue, object)
Example to use:
使用示例:
resolvePath(window,'document.body') => <body>
resolvePath(window,'document.body.xyz') => undefined
resolvePath(window,'document.body.xyz', null) => null
resolvePath(window,'document.body.xyz', 1) => 1
Bonus:
奖金:
To seta path (Requested by @rob-gordon) you can use:
要设置路径(由@rob-gordon 请求),您可以使用:
const setPath = (object, path, value) => path
.split('.')
.reduce((o,p,i) => o[p] = path.split('.').length === ++i ? value : o[p] || {}, object)
Example:
例子:
let myVar = {}
setPath(myVar, 'a.b.c', 42) => 42
console.log(myVar) => {a: {b: {c: 42}}}
Access array with []:
使用 [] 访问数组:
const resolvePath = (object, path, defaultValue) => path
.split(/[\.\[\]\'\"]/)
.filter(p => p)
.reduce((o, p) => o ? o[p] : defaultValue, object)
Example:
例子:
const myVar = {a:{b:[{c:1}]}}
resolvePath(myVar,'a.b[0].c') => 1
resolvePath(myVar,'a["b"][\'0\'].c') => 1
回答by Felix Kling
You'd have to parse the string yourself:
您必须自己解析字符串:
function getProperty(obj, prop) {
var parts = prop.split('.');
if (Array.isArray(parts)) {
var last = parts.pop(),
l = parts.length,
i = 1,
current = parts[0];
while((obj = obj[current]) && i < l) {
current = parts[i];
i++;
}
if(obj) {
return obj[last];
}
} else {
throw 'parts is not valid array';
}
}
This required that you also define array indexes with dot notation:
这要求您还使用点表示法定义数组索引:
var part3name1 = "part3.0.name";
It makes the parsing easier.
它使解析更容易。
回答by TheZver
Works for arrays / arrays inside the object also. Defensive against invalid values.
也适用于对象内的数组/数组。防御无效值。
/**
* Retrieve nested item from object/array
* @param {Object|Array} obj
* @param {String} path dot separated
* @param {*} def default value ( if result undefined )
* @returns {*}
*/
function path(obj, path, def){
var i, len;
for(i = 0,path = path.split('.'), len = path.length; i < len; i++){
if(!obj || typeof obj !== 'object') return def;
obj = obj[path[i]];
}
if(obj === undefined) return def;
return obj;
}
//////////////////////////
// TEST //
//////////////////////////
var arr = [true, {'sp ace': true}, true]
var obj = {
'sp ace': true,
arr: arr,
nested: {'dotted.str.ing': true},
arr3: arr
}
shouldThrow(`path(obj, "arr.0")`);
shouldBeDefined(`path(obj, "arr[0]")`);
shouldBeEqualToNumber(`path(obj, "arr.length")`, 3);
shouldBeTrue(`path(obj, "sp ace")`);
shouldBeEqualToString(`path(obj, "none.existed.prop", "fallback")`, "fallback");
shouldBeTrue(`path(obj, "nested['dotted.str.ing'])`);
<script src="https://cdn.rawgit.com/coderek/e7b30bac7634a50ad8fd/raw/174b6634c8f57aa8aac0716c5b7b2a7098e03584/js-test.js"></script>
回答by Shanimal
using eval:
使用评估:
var part1name = eval("someObject.part1.name");
wrap to return undefined on error
换行以在出错时返回 undefined
function path(obj, path) {
try {
return eval("obj." + path);
} catch(e) {
return undefined;
}
}
http://jsfiddle.net/shanimal/b3xTw/
http://jsfiddle.net/shanimal/b3xTw/
Please use common sense and caution when wielding the power of eval. It's a bit like a light saber, if you turn it on there's a 90% chance you'll sever a limb. Its not for everybody.
在运用 eval 的力量时,请使用常识和谨慎。它有点像一把光剑,如果你打开它,你有 90% 的机会会切断一条肢体。它不适合所有人。
回答by Harish Anchu
You can manage to obtain value of a deep object member with dot notation without any external JavaScript library with the simple following trick:
您可以通过以下简单的技巧,在没有任何外部 JavaScript 库的情况下,使用点表示法获取深层对象成员的值:
new Function('_', 'return _.' + path)(obj);
In your case to obtain value of part1.name
from someObject
just do:
在您的情况下,要获取part1.name
from 的值,someObject
请执行以下操作:
new Function('_', 'return _.part1.name')(someObject);
Here is a simple fiddle demo: https://jsfiddle.net/harishanchu/oq5esowf/
这是一个简单的小提琴演示:https: //jsfiddle.net/harishanchu/oq5esowf/
回答by Nick Grealy
This will probably never see the light of day... but here it is anyway.
这可能永远不会见天日......但无论如何它在这里。
- Replace
[]
bracket syntax with.
- Split on
.
character - Remove blank strings
- Find the path (otherwise
undefined
)
- 将
[]
括号语法替换为.
- 按
.
字符拆分 - 删除空字符串
- 找到路径(否则
undefined
)
// "one liner" (ES6)
const deep_value = (obj, path) =>
path
.replace(/\[|\]\.?/g, '.')
.split('.')
.filter(s => s)
.reduce((acc, val) => acc && acc[val], obj);
// ... and that's it.
var someObject = {
'part1' : {
'name': 'Part 1',
'size': '20',
'qty' : '50'
},
'part2' : {
'name': 'Part 2',
'size': '15',
'qty' : '60'
},
'part3' : [
{
'name': 'Part 3A',
'size': '10',
'qty' : '20'
}
// ...
]
};
console.log(deep_value(someObject, "part1.name")); // Part 1
console.log(deep_value(someObject, "part2.qty")); // 60
console.log(deep_value(someObject, "part3[0].name")); // Part 3A
回答by James
It's a one liner with lodash.
这是一个带有 lodash 的单衬里。
const deep = { l1: { l2: { l3: "Hello" } } };
const prop = "l1.l2.l3";
const val = _.reduce(prop.split('.'), function(result, value) { return result ? result[value] : undefined; }, deep);
// val === "Hello"
Or even better...
或者更好...
const val = _.get(deep, prop);
Or ES6 version w/ reduce...
或 ES6 版本带减少...
const val = prop.split('.').reduce((r, val) => { return r ? r[val] : undefined; }, deep);