How can I access and process nested objects, arrays or JSON?
我有一个包含对象和数组的嵌套数据结构。如何提取信息,即访问特定或多个值(或键)?
例如:
1 2 3 4 5 6 7 8 9 10 | var data = { code: 42, items: [{ id: 1, name: 'foo' }, { id: 2, name: 'bar' }] }; |
如何访问
预赛
javascript只有一种数据类型,可以包含多个值:object。数组是对象的一种特殊形式。好的。
(普通)对象具有以下形式好的。
1 | {key: value, key: value, ...} |
数组的形式为好的。
1 | [value, value, ...] |
数组和对象都公开了
可以使用点表示法访问属性好的。
1 | const value = obj.someProperty; |
或括号表示法,如果属性名不是有效的javascript标识符名称[规范],或者名称是变量的值:好的。
1 2 3 4 5 6 | // the space is not a valid character in identifier names const value = obj["some Property"]; // property name as variable const name ="some Property"; const value = obj[name]; |
因此,只能使用括号表示法访问数组元素:好的。4等待。。。JSON怎么样?
JSON是数据的文本表示,就像XML、YAML、CSV等。要使用这些数据,首先必须将其转换为JavaScript数据类型,即数组和对象(以及如何使用这些数据类型,我们刚刚解释过)。如何解析JSON在javascript中解析JSON这个问题中进行了解释?.好的。进一步阅读材料
如何访问数组和对象是基本的javascript知识,因此最好阅读MDN javascript指南,尤其是章节好的。
- 使用对象
- 数组
- 雄辩的javascript-数据结构
访问嵌套数据结构
嵌套数据结构是指引用其他数组或对象的数组或对象,即其值是数组或对象。这种结构可以通过连续应用点或括号来访问。好的。
下面是一个例子:好的。
1 2 3 4 5 6 7 8 9 10 | const data = { code: 42, items: [{ id: 1, name: 'foo' }, { id: 2, name: 'bar' }] }; |
假设我们要访问第二项的
我们可以一步一步地做到这一点:好的。
如我们所见,
1 | data.items |
该值是一个数组,要访问它的第二个元素,我们必须使用括号表示法:好的。
1 | data.items[1] |
这个值是一个对象,我们再次使用点表示法访问
1 | const item_name = data.items[1].name; |
或者,我们可以对任何属性使用括号表示法,特别是如果名称包含使其对点表示法无效的字符:好的。
1 | const item_name = data['items'][1]['name']; |
我正试图进入一个房产,但我只得到了1(5)回?
大多数情况下,当您获取
1 2 | const foo = {bar: {baz: 42}}; console.log(foo.baz); // undefined |
使用
1 | console.log(foo.bar.baz); // 42 |
如果属性名是动态的,而我事先不知道它们怎么办?
如果属性名未知,或者我们想要访问数组的对象/元素的所有属性,我们可以使用对象的
物体好的。
为了遍历
1 2 3 4 5 | for (const prop in data) { // `prop` contains the name of each property, i.e. `'code'` or `'items'` // consequently, `data[prop]` refers to the value of each property, i.e. // either `42` or the array } |
根据对象的来源(以及您想做什么),您可能需要在每次迭代中测试该属性是该对象的真正属性还是继承的属性。你可以用
作为使用
1 2 3 4 | Object.keys(data).forEach(function(prop) { // `prop` is the property name // `data[prop]` is the property value }); |
数组好的。
为了迭代
1 2 3 4 5 6 7 8 9 10 | for(let i = 0, l = data.items.length; i < l; i++) { // `i` will take on the values `0`, `1`, `2`,..., i.e. in each iteration // we can access the next element in the array with `data.items[i]`, example: // // var obj = data.items[i]; // // Since each element is an object (in our example), // we can now access the objects properties with `obj.id` and `obj.name`. // We could also use `data.items[i].id`. } |
我们也可以使用
随着ECMAScript 5浏览器支持的增加,数组方法
在支持ES2015(ES6)的环境中,您还可以使用EDOCX1[11]mdn循环,该循环不仅适用于阵列,而且适用于任何不可重复的:好的。
1 2 3 | for (const item of data.items) { // `item` is the array element, **not** the index } |
在每次迭代中,
除了未知键之外,数据结构的"深度"(即有多少嵌套对象)也可能是未知的。如何访问深度嵌套的属性通常取决于具体的数据结构。好的。
但是,如果数据结构包含重复模式,例如二进制树的表示,则解决方案通常包括递归[维基百科]访问数据结构的每个级别。好的。
下面是获取二叉树的第一个叶节点的示例:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 | function getLeaf(node) { if (node.leftChild) { return getLeaf(node.leftChild); // <- recursive call } else if (node.rightChild) { return getLeaf(node.rightChild); // <- recursive call } else { // node must be a leaf node return node; } } const first_leaf = getLeaf(root); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | const root = { leftChild: { leftChild: { leftChild: null, rightChild: null, data: 42 }, rightChild: { leftChild: null, rightChild: null, data: 5 } }, rightChild: { leftChild: { leftChild: null, rightChild: null, data: 6 }, rightChild: { leftChild: null, rightChild: null, data: 7 } } }; function getLeaf(node) { if (node.leftChild) { return getLeaf(node.leftChild); } else if (node.rightChild) { return getLeaf(node.rightChild); } else { // node must be a leaf node return node; } } console.log(getLeaf(root).data); |
好的。
访问具有未知键和深度的嵌套数据结构的更通用的方法是测试值的类型并相应地执行操作。好的。
下面是一个示例,它将嵌套数据结构中的所有基元值添加到数组中(假设它不包含任何函数)。如果我们遇到一个对象(或数组),我们只需在该值上再次调用
1 2 3 4 5 6 7 8 9 10 11 12 13 | function toArray(obj) { const result = []; for (const prop in obj) { const value = obj[prop]; if (typeof value === 'object') { result.push(toArray(value)); // <- recursive call } else { result.push(value); } } return result; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | const data = { code: 42, items: [{ id: 1, name: 'foo' }, { id: 2, name: 'bar' }] }; function toArray(obj) { const result = []; for (const prop in obj) { const value = obj[prop]; if (typeof value === 'object') { result.push(toArray(value)); } else { result.push(value); } } return result; } console.log(toArray(data)); |
好的。帮手
由于复杂对象或数组的结构不一定明显,因此我们可以检查每个步骤的值,以决定如何进一步移动。
1 2 | > console.log(data.items) [ Object, Object ] |
这里我们看到,
1 2 3 4 5 | > console.log(data.items[1]) Object id: 2 name:"bar" __proto__: Object |
这告诉我们,
你可以用这种方式进入
1 | data.items[1].name |
或
1 | data["items"][1]["name"] |
两者都是平等的。
如果您试图通过
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | var data = { code: 42, items: [{ id: 1, name: 'foo' }, { id: 2, name: 'bar' }] }; _.find(data.items, function(item) { return item.id === 2; }); // Object {id: 2, name:"bar"} |
根据我的经验,使用高阶函数而不是
只有我的2美分。
有时,需要使用字符串访问嵌套对象。例如,简单的方法是第一层
1 2 3 | var obj = { hello:"world" }; var key ="hello"; alert(obj[key]);//world |
但对于复杂的JSON,情况往往不是这样。随着JSON变得更加复杂,在JSON内部查找值的方法也变得复杂。用于导航JSON的递归方法是最好的,并且如何利用递归将取决于要搜索的数据类型。如果涉及到条件语句,JSON搜索可能是一个很好的工具。
如果正在访问的属性已经已知,但路径很复杂,例如在此对象中
1 2 3 4 5 6 7 | var obj = { arr: [ { id: 1, name:"larry" }, { id: 2, name:"curly" }, { id: 3, name:"moe" } ] }; |
你知道你想得到对象中数组的第一个结果,也许你想使用
1 | var moe = obj["arr[0].name"]; |
但是,这将导致异常,因为没有具有该名称的对象的属性。能够使用它的解决方案是将对象的树方面展平。这可以递归地完成。
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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; } |
现在,复杂的物体可以展平
1 2 3 | var obj = previous definition; var flat = flatten(obj); var moe = flat["arr[0].name"];//moe |
这里是使用这种方法的
对象和数组有许多内置方法,可以帮助您处理数据。
注意:在许多示例中,我使用的是箭头函数。它们与函数表达式类似,但在词汇上绑定
1 2 3 4 5 6 7 8 9 | const obj = { a: 1 ,b: 2 ,c: 3 } console.log(Object.keys(obj)) // ['a', 'b', 'c'] console.log(Object.values(obj)) // [1, 2, 3] console.log(Object.entries(obj)) // [['a', 1], ['b', 2], ['c', 3]] |
1 2 3 4 5 6 7 8 9 | const obj = { a: 1 ,b: 2 ,c: 3 } for (const [key, value] of Object.entries(obj)) { console.log(`key: ${key}, value: ${value}`) } |
用for-of循环和析构化赋值迭代
for-of循环允许迭代数组元素。语法是
析构函数赋值允许您从数组或对象中提取值并将其赋给变量。在这种情况下,
1 2 3 4 | for (const element of Object.entries(obj)) { const key = element[0] ,value = element[1] } |
正如你所看到的,破坏使这变得简单多了。
如果指定的回调函数为数组的每个元素返回
1 2 3 4 5 6 7 8 9 10 | const arr = [1, 2, 3] // true, because every element is greater than 0 console.log(arr.every(x => x > 0)) // false, because 3^2 is greater than 5 console.log(arr.every(x => Math.pow(x, 2) < 5)) // true, because 2 is even (the remainder from dividing by 2 is 0) console.log(arr.some(x => x % 2 === 0)) // false, because none of the elements is equal to 5 console.log(arr.some(x => x === 5)) |
4
1 2 3 4 5 6 7 | const arr = [1, 2, 3] console.log(arr.map(x => x + 1)) // [2, 3, 4] console.log(arr.map(x => String.fromCharCode(96 + x))) // ['a', 'b', 'c'] console.log(arr.map(x => x)) // [1, 2, 3] (no-op) console.log(arr.map(x => Math.pow(x, 2))) // [1, 4, 9] console.log(arr.map(String)) // ['1', '2', '3'] |
1 2 3 4 5 6 | const arr = [1, 2, 3] // Sum of array elements. console.log(arr.reduce((a, b) => a + b)) // 6 // The largest number in the array. console.log(arr.reduce((a, b) => a > b ? a : b)) // 3 |
1 2 3 4 5 | const sum = arr => arr.reduce((a, b) => a + b, 0) console.log(sum([])) // 0 console.log(sum([4])) // 4 console.log(sum([2, 5])) // 7 |
这个问题很古老,所以是当代的更新。随着ES2015的开始,有其他方法可以获取您需要的数据。现在有一个称为对象销毁的特性,用于访问嵌套的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | const data = { code: 42, items: [{ id: 1, name: 'foo' }, { id: 2, name: 'bar' }] }; const { items: [, { name: secondName }] } = data; console.log(secondName); |
上面的例子从名为
值得注意的是,对于本例来说,它可能是杀伤力过大的,因为简单的数组访问更容易读取,但是它在分解对象时非常有用。
这是对您的特定用例的非常简短的介绍,首先,析构函数可能是一种不寻常的语法,需要习惯。我建议阅读Mozilla的销毁分配文档以了解更多信息。
要访问嵌套属性,需要指定其名称,然后搜索对象。
如果您已经知道确切的路径,那么您可以像这样在脚本中对其进行硬编码:
1 | data['items'][1]['name'] |
这些也有用-
1 2 3 | data.items[1].name data['items'][1].name data.items[1]['name'] |
当你在手前不知道确切的名字时,或者一个用户是为你提供名字的人。然后需要通过数据结构进行动态搜索。这里有人建议可以使用
路径是这样说的:首先使用键
如果你有一条很长的路,你甚至可以使用
1 | 'items.1.name'.split('.').reduce((a,v) => a[v], data) |
这只是简单的javascript,不使用任何第三方库,如jquery或lodash。
如果您愿意包括一个库,那么使用jsonpath将是最灵活的解决方案之一:https://github.com/s3u/jsonpath(节点和浏览器)
对于您的用例,JSON路径是:
1 | $..items[1].name |
所以:
4您可以使用
1 2 3 4 | var object = { 'a': [{ 'b': { 'c': 3 } }] }; _.get(object, 'a[0].b.c'); // => 3 |
我更喜欢jquery。它干净易读。
1 2 3 | $.each($.parseJSON(data), function (key, value) { alert(value.<propertyname>); }); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | var ourStorage = { "desk": { "drawer":"stapler" }, "cabinet": { "top drawer": { "folder1":"a file", "folder2":"secrets" }, "bottom drawer":"soda" } }; ourStorage.cabinet["top drawer"].folder2; // Outputs ->"secrets" |
或
1 | //parent.subParent.subsubParent["almost there"]["final property"] |
基本上,在每个展开在其下面的后代之间使用一个点,当您有由两个字符串组成的对象名时,必须使用["obj name"]符号。否则,只要一个点就足够了;
来源:https://learn.freecodecamp.org/javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects
要添加到该列表中,访问嵌套数组的方式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | var ourPets = [ { animalType:"cat", names: [ "Meowzer", "Fluffy", "Kit-Cat" ] }, { animalType:"dog", names: [ "Spot", "Bowser", "Frankie" ] } ]; ourPets[0].names[1]; // Outputs"Fluffy" ourPets[1].names[0]; // Outputs"Spot" |
来源:https://learn.freecodecamp.org/javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-array/
另一个更有用的描述上述情况的文档:https://developer.mozilla.org/en-us/docs/learn/javascript/objects/basics括号符号
老问题,但正如没人提到的罗达什(只是强调)。
如果您已经在项目中使用了lodash,我认为在一个复杂的示例中使用它是一种很好的方法:
选择1
1 | _.get(response, ['output', 'fund', 'data', '0', 'children', '0', 'group', 'myValue'], '') |
等同于:
选择2
1 | response.output.fund.data[0].children[0].group.myValue |
第一个和第二个选项的区别在于,在opt 1中,如果路径中缺少(未定义)某个属性,则不会出现错误,它会返回第三个参数。
对于阵列滤波器,lodash有
我希望这对谁在寻找处理标题所暗示的真正复杂数据的选项有用。
如果要查找满足特定条件的一个或多个对象,可以使用查询JS选择几个选项。
1 2 3 4 5 6 7 | //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:""}); |
还有一个
对于查询JS的进一步解释,您可以从本文开始。
下划线JS方式
这是一个javascript库,它提供了大量有用的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | var data = { code: 42, items: [{ id: 1, name: 'foo' }, { id: 2, name: 'bar' }] }; var item = _.findWhere(data.items, { id: 2 }); if (!_.isUndefined(item)) { console.log('NAME =>', item.name); } //using find - var item = _.find(data.items, function(item) { return item.id === 2; }); if (!_.isUndefined(item)) { console.log('NAME =>', item.name); } |
我不认为提问者只关心一个层次的嵌套对象,所以我演示了下面的演示来演示如何访问深度嵌套JSON对象的节点。好吧,让我们找到ID为"5"的节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | var data = { 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' }] }] }; var jsonloop = new JSONLoop(data, 'id', 'items'); jsonloop.findNodeById(data, 5, function(err, node) { if (err) { document.write(err); } else { document.write(JSON.stringify(node, null, 2)); } }); |
1 | <script src="https://rawgit.com/dabeng/JSON-Loop/master/JSONLoop.js"> |
动态访问多级对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | var obj = { name:"salut", subobj: { subsubobj: { names:"I am sub sub obj" } } }; var level ="subobj.subsubobj.names"; level = level.split("."); var currentObjState = obj; for (var i = 0; i < level.length; i++) { currentObjState = currentObjState[level[i]]; } console.log(currentObjState); |
工作小提琴:https://jsfiddle.net/anderitodorut/3mws3kjl/
为了以防万一,任何人在2017年或以后访问这个问题,并寻找一种简单易记的方式,这里有一篇关于访问javascript中嵌套对象而不被欺骗的详细博客文章
无法读取未定义错误的属性"foo"
1。奥利弗·斯蒂尔的嵌套对象访问模式最简单、最干净的方法是使用OliverSteele的嵌套对象访问模式。
1 | const name = ((user || {}).personalInfo || {}).name; |
用这个符号,你永远不会碰到
无法读取未定义的属性"name"。
基本上检查用户是否存在,如果不存在,则动态创建一个空对象。这样,下一级键将始终从存在的对象或空对象访问,但从不从未定义的对象访问。
2。使用数组减少访问嵌套对象为了能够访问嵌套数组,您可以编写自己的数组reduce util。
1 2 3 4 5 6 7 8 9 10 11 | const getNestedObject = (nestedObj, pathArr) => { return pathArr.reduce((obj, key) => (obj && obj[key] !== 'undefined') ? obj[key] : undefined, nestedObj); } // pass in your object structure as array elements const name = getNestedObject(user, ['personalInfo', 'name']); // to access nested array, just pass in array index as an element the path array. const city = getNestedObject(user, ['personalInfo', 'addresses', 0, 'city']); // this will return the city from the first address item. |
还有一个优秀的类型处理最小的库类型,可以为您完成所有这一切。
一种用于分解任意JSON树的pythonic、递归和函数方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | handlers = { list: iterate, dict: delve, str: emit_li, float: emit_li, } def emit_li(stuff, strong=False): emission = ' <li> %s </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) |
其中,数据是一个python列表(从JSON文本字符串中解析):
1 2 3 4 5 6 7 8 9 10 11 12 | 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']} ] |
jquery的grep函数允许您通过数组进行过滤:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | var data = { code: 42, items: [{ id: 1, name: 'foo' }, { id: 2, name: 'bar' }] }; $.grep(data.items, function(item) { if (item.id === 2) { console.log(item.id); //console id of item console.log(item.name); //console name of item console.log(item); //console item object return item; //returns item object } }); // Object {id: 2, name:"bar"} |
1 | <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"> |
我的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | var stringjson={ "success": true, "message":"working", "data": [{ "id": 1, "name":"foo" }] }; var obj=JSON.parse(stringjson); var key ="message"; alert(obj[key]); var keyobj ="data"; var ArrObj =obj[keyobj]; alert(ArrObj[0].id); |
使用火山灰是很好的解决办法
前任:
1 2 3 | var object = { 'a': { 'b': { 'c': 3 } } }; _.get(object, 'a.b.c'); // => 3 |