我有一个包含对象和数组的嵌套数据结构。我如何提取信息,即访问一个特定的或多个值(或键)?
例如:
var data = {
code: 42,
items: [{
id: 1,
name: 'foo'
}, {
id: 2,
name: 'bar'
}]
};
我如何访问项目中的第二个项目的名称?
我有一个包含对象和数组的嵌套数据结构。我如何提取信息,即访问一个特定的或多个值(或键)?
例如:
var data = {
code: 42,
items: [{
id: 1,
name: 'foo'
}, {
id: 2,
name: 'bar'
}]
};
我如何访问项目中的第二个项目的名称?
当前回答
python式、递归式和函数式方法来分解任意JSON树:
handlers = {
list: iterate,
dict: delve,
str: emit_li,
float: emit_li,
}
def emit_li(stuff, strong=False):
emission = '<li><strong>%s</strong></li>' if strong else '<li>%s</li>'
print(emission % stuff)
def iterate(a_list):
print('<ul>')
map(unravel, a_list)
print('</ul>')
def delve(a_dict):
print('<ul>')
for key, value in a_dict.items():
emit_li(key, strong=True)
unravel(value)
print('</ul>')
def unravel(structure):
h = handlers[type(structure)]
return h(structure)
unravel(data)
其中data是python列表(从JSON文本字符串解析而来):
data = [
{'data': {'customKey1': 'customValue1',
'customKey2': {'customSubKey1': {'customSubSubKey1': 'keyvalue'}}},
'geometry': {'location': {'lat': 37.3860517, 'lng': -122.0838511},
'viewport': {'northeast': {'lat': 37.4508789,
'lng': -122.0446721},
'southwest': {'lat': 37.3567599,
'lng': -122.1178619}}},
'name': 'Mountain View',
'scope': 'GOOGLE',
'types': ['locality', 'political']}
]
其他回答
如果你正在寻找一个或多个符合特定条件的对象,你可以使用query-js进行一些选择
//will return all elements with an id larger than 1
data.items.where(function(e){return e.id > 1;});
//will return the first element with an id larger than 1
data.items.first(function(e){return e.id > 1;});
//will return the first element with an id larger than 1
//or the second argument if non are found
data.items.first(function(e){return e.id > 1;},{id:-1,name:""});
还有一个single和一个singleOrDefault,它们的工作类似于firstand firstordefault。唯一的区别是,如果找到了不止一根火柴,他们就会扔。
关于query-js的进一步解释可以从这篇文章开始
下面是一个使用对象扫描的答案。
当访问单个条目时,这个答案并没有提供比普通javascript更多的好处。然而,同时与多个字段交互,这个答案可以更有效。
下面是与单个字段交互的方法
// const objectScan = require('object-scan'); const data = { code: 42, items: [{ id: 1, name: 'foo' }, { id: 2, name: 'bar' }] }; const get = (haystack, needle) => objectScan([needle], { abort: true, rtn: 'value' })(haystack); const set = (haystack, needle, value) => objectScan([needle], { abort: true, rtn: 'bool', filterFn: ({ parent, property }) => { parent[property] = value; return true; } })(haystack); console.log(get(data, 'items[1].name')); // => bar console.log(set(data, 'items[1].name', 'foo2')); // => true console.log(data); // => { code: 42, items: [ { id: 1, name: 'foo' }, { id: 2, name: 'foo2' } ] } .as-console-wrapper {max-height: 100% !important; top: 0} <script src="https://bundle.run/object-scan@13.8.0"></script>
声明:我是object-scan的作者
下面是如何同时与多个字段交互
// const objectScan = require('object-scan'); const data = { code: 42, items: [{ id: 1, name: 'foo' }, { id: 2, name: 'bar' }] }; const get = (haystack, ...needles) => objectScan(needles, { joined: true, rtn: 'entry' })(haystack); const set = (haystack, actions) => objectScan(Object.keys(actions), { rtn: 'count', filterFn: ({ matchedBy, parent, property }) => { matchedBy.forEach((m) => { parent[property] = actions[m]; }) return true; } })(haystack); console.log(get(data, 'items[0].name', 'items[1].name')); // => [ [ 'items[1].name', 'bar' ], [ 'items[0].name', 'foo' ] ] console.log(set(data, { 'items[0].name': 'foo1', 'items[1].name': 'foo2' })); // => 2 console.log(data); // => { code: 42, items: [ { id: 1, name: 'foo1' }, { id: 2, name: 'foo2' } ] } .as-console-wrapper {max-height: 100% !important; top: 0} <script src="https://bundle.run/object-scan@13.8.0"></script>
声明:我是object-scan的作者
下面是如何在一个深度嵌套的对象中通过id搜索找到一个实体(正如在评论中问到的那样)
// const objectScan = require('object-scan'); const myData = { code: 42, items: [{ id: 1, name: 'aaa', items: [{ id: 3, name: 'ccc' }, { id: 4, name: 'ddd' }] }, { id: 2, name: 'bbb', items: [{ id: 5, name: 'eee' }, { id: 6, name: 'fff' }] }] }; const findItemById = (haystack, id) => objectScan(['**(^items$).id'], { abort: true, useArraySelector: false, rtn: 'parent', filterFn: ({ value }) => value === id })(haystack); console.log(findItemById(myData, 5)); // => { id: 5, name: 'eee' } .as-console-wrapper {max-height: 100% !important; top: 0} <script src="https://bundle.run/object-scan@13.8.0"></script>
声明:我是object-scan的作者
要访问嵌套属性,需要指定其名称,然后搜索对象。
如果你已经知道确切的路径,那么你可以像这样在你的脚本中硬编码它:
data['items'][1]['name']
这些也有用——
data.items[1].name
data['items'][1].name
data.items[1]['name']
如果您事先不知道确切的名称,或者用户是为您提供名称的人。然后需要动态搜索数据结构。有些人建议可以使用for循环来完成搜索,但是有一种非常简单的方法可以使用Array.reduce遍历路径。
const data = { code: 42, items: [{ id: 1, name: 'foo' }, { id: 2, name: 'bar' }] }
const path = [ 'items', '1', 'name']
let result = path.reduce((a,v) => a[v], data)
路径的意思是:首先获取带有关键项的对象,它恰好是一个数组。然后取第1个元素(0个索引数组)。最后取该数组元素中键名的对象,恰好是字符串bar。
如果路径很长,甚至可以使用String。分开让这一切更容易-
'items.1.name'.split('.').reduce((a,v) => a[v], data)
这只是简单的JavaScript,没有使用任何第三方库,如jQuery或lodash。
有时,使用字符串访问嵌套对象是可取的。例如,最简单的方法是第一级
var obj = { hello: "world" };
var key = "hello";
alert(obj[key]);//world
但复杂的json通常不是这样。随着json变得越来越复杂,在json中查找值的方法也变得越来越复杂。导航json的递归方法是最好的,如何利用递归取决于要搜索的数据类型。如果涉及到条件语句,json搜索可能是一个很好的工具。
如果已经知道要访问的属性,但是路径很复杂,例如在这个对象中
var obj = {
arr: [
{ id: 1, name: "larry" },
{ id: 2, name: "curly" },
{ id: 3, name: "moe" }
]
};
你知道你想要得到对象中数组的第一个结果,也许你想使用
var moe = obj["arr[0].name"];
然而,这将导致一个异常,因为对象没有该名称的属性。能够使用这种方法的解决方案是将对象的树形面平直。这可以递归完成。
function flatten(obj){
var root = {};
(function tree(obj, index){
var suffix = toString.call(obj) == "[object Array]" ? "]" : "";
for(var key in obj){
if(!obj.hasOwnProperty(key))continue;
root[index+key+suffix] = obj[key];
if( toString.call(obj[key]) == "[object Array]" )tree(obj[key],index+key+suffix+"[");
if( toString.call(obj[key]) == "[object Object]" )tree(obj[key],index+key+suffix+".");
}
})(obj,"");
return root;
}
现在,这个复杂的物体可以被平面化
var obj = previous definition;
var flat = flatten(obj);
var moe = flat["arr[0].name"];//moe
下面是使用这种方法的jsFiddle演示。
// const path = 'info.value[0].item'
// const obj = { info: { value: [ { item: 'it works!' } ], randominfo: 3 } }
// getValue(path, obj)
export const getValue = ( path , obj) => {
const newPath = path.replace(/\]/g, "")
const arrayPath = newPath.split(/[\[\.]+/) || newPath;
const final = arrayPath.reduce( (obj, k) => obj ? obj[k] : obj, obj)
return final;
}