访问/进程(嵌套)对象,数组或JSON

我有一个(嵌套的)数据结构包含对象和数组。 我如何提取信息,即访问特定的或多个值(或键)?

例如:

var data = { code: 42, items: [{ id: 1, name: 'foo' }, { id: 2, name: 'bar' }] }; 

我怎样才能访问项目中的第二个项目的name

预赛

JavaScript只有一个数据types可以包含多个值: Object数组是一种特殊的对象forms。

(普通)对象有forms

 {key: value, key: value, ...} 

数组有forms

 [value, value, ...] 

数组和对象都暴露了一个key -> value结构。 数组中的键必须是数字,而任何string都可以用作对象中的键。 键值对也称为“属性”

属性可以使用点符号来访问

 const value = obj.someProperty; 

括号表示 ,如果属性名称不是有效的JavaScript 标识符名称[spec] ,或者名称是variables的值:

 // 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]; 

出于这个原因,数组元素只能使用括号表示来访问:

 const value = arr[5]; // arr.5 would be a syntax error // property name / index as variable const x = 5; const value = arr[x]; 

等等… JSON呢?

JSON是数据的文本表示,就像XML,YAML,CSV等一样。 要处理这些数据,首先必须将其转换为JavaScript数据types,即数组和对象(以及如何处理这些数据)。 如何parsingJSON在JavaScriptparsingJSON的问题中解释? 。

更多的阅读材料

如何访问数组和对象是基本的JavaScript知识,因此build议阅读MDN JavaScript指南 ,特别是章节

  • 使用对象
  • 数组
  • 雄辩的JavaScript – 数据结构


访问嵌套的数据结构

嵌套数据结构是引用其他数组或对象的数组或对象,即其值是数组或对象。 这样的结构可以通过连续应用点或括号来访问。

这里是一个例子:

 const data = { code: 42, items: [{ id: 1, name: 'foo' }, { id: 2, name: 'bar' }] }; 

假设我们想要访问第二个项目的name

以下是我们如何一步一步做到的:

正如我们所看到的, data是一个对象,因此我们可以使用点符号来访问它的属性。 items属性的访问方式如下:

 data.items 

该值是一个数组,要访问它的第二个元素,我们必须使用括号表示法:

 data.items[1] 

这个值是一个对象,我们再次使用点符号来访问name属性。 所以我们最终得到:

 const item_name = data.items[1].name; 

或者,我们可以使用任何属性的括号表示法,特别是如果名称中包含的字符会使点符号的用法无效:

 const item_name = data['items'][1]['name']; 

我试图访问一个属性,但我只得到undefined回来?

大多数情况下,当你得到undefined ,对象/数组根本就没有这个名字的属性。

 const foo = {bar: {baz: 42}}; console.log(foo.baz); // undefined 

使用console.logconsole.dir并检查对象/数组的结构。 您尝试访问的属性可能实际上是在嵌套的对象/数组上定义的。

 console.log(foo.bar.baz); // 42 

如果这些属性名称是dynamic的,而事先不知道它们呢?

如果属性名称是未知的,或者我们想访问一个数组的对象/元素的所有属性,我们可以for...in [MDN]循环中使用for...in作为对象, for [MDN]循环用于数组迭代属性/元素。

对象

为了迭代data所有属性,我们可以像这样迭代对象

 for (const prop in data) { // `prop` contains the name of each property, ie `'code'` or `'items'` // consequently, `data[prop]` refers to the value of each property, ie // either `42` or the array } 

根据对象来自哪里(以及你想要做什么),你可能必须在每次迭代中testing属性是否真的是对象的一个​​属性,或者它是一个inheritance的属性。 你可以用Object#hasOwnProperty [MDN]来做到这一点。

作为hasOwnProperty for...in替代方法,可以使用Object.keys [MDN]获取一组属性名称

 Object.keys(data).forEach(function(prop) { // `prop` is the property name // `data[prop]` is the property value }); 

数组

要迭代data.items 数组的所有元素,我们使用for循环:

 for(let i = 0, l = data.items.length; i < l; i++) { // `i` will take on the values `0`, `1`, `2`,..., ie 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`. } 

也可以使用for...in来迭代数组,但是有一些原因可以避免这种情况: 为什么在数组中的“for(var item in list)”数组被认为是不好的练习? 。

随着对ECMAScript 5的浏览器支持的增加, forEach [MDN]的数组方法也成为一个有趣的select:

 data.items.forEach(function(value, index, array) { // The callback is executed for each element in the array. // `value` is the element itself (equivalent to `array[index]`) // `index` will be the index of the element in the array // `array` is a reference to the array itself (ie `data.items` in this case) }); 

在支持ES2015(ES6)的环境中,还可以使用[MDN]循环的for...of ,它不仅适用于数组,而且适用于任何迭代

 for (const item of data.items) { // `item` is the array element, **not** the index } 

在每次迭代中, for...of直接给我们下一个迭代元素,没有“索引”来访问或使用。


如果我不知道数据结构的“深度”是什么?

除了未知的键,数据结构的“深度”(即有多less嵌套的对象)也可能是未知的。 如何访问深度嵌套的属性通常取决于确切的数据结构。

但是,如果数据结构包含重复模式,例如二叉树的表示,则解决scheme通常包括recursion [Wikipedia]访问数据结构的每个级别。

以下是获取二叉树的第一个叶节点的示例:

 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); 
 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); 

你可以这样访问它

 data.items[1].name 

要么

 data["items"][1]["name"] 

两种方式是平等的。

如果你试图通过idname访问示例结构中的一个item ,而不知道它在数组中的位置,最简单的方法是使用underscore.js库:

 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"} 

根据我的经验,使用高阶函数而不是forfor..in循环会导致代码更容易推理,因此更易于维护。

只是我2美分。

有时,使用string访问嵌套对象可能是可取的。 例如,简单的方法是第一级

 var obj = { hello: "world" }; var key = "hello"; alert(obj[key]);//world 

但是复杂的json往往不是这种情况。 随着json变得越来越复杂,寻找json内部值的方法也变得复杂。 用于浏览json的recursion方法是最好的,以及如何利用recursion将取决于正在search的数据的types。 如果涉及到条件语句,则jsonsearch可能是一个很好的工具。

如果被访问的属性已经是已知的,但path是复杂的,例如在这个对象中

 var obj = { arr: [ { id: 1, name: "larry" }, { id: 2, name: "curly" }, { id: 3, name: "moe" } ] }; 

而且你知道你想获得数组的第一个结果的对象,也许你想使用

 var moe = obj["arr[0].name"]; 

但是,这将导致一个exception,因为没有该名称的对象的属性。 能够使用这个的解决scheme是平坦化对象的树状方面。 这可以recursion地完成。

 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 Demo

对象和数组有很多内置的方法可以帮助你处理数据。

注意:在很多例子中,我使用了箭头函数 。 它们类似于函数expression式 ,但是它们在词汇上绑定了this值。

Object.keys()Object.values() (ES 2017)和Object.entries() (ES 2017)

Object.keys()返回对象键的数组, Object.values()返回对象值的数组, Object.entries()[key, value]的格式返回对象键和相应值的数组。

 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]] 

这个问题是相当古老的,作为一个当代的更新。 随着ES2015的发布,您可以select持有您所需的数据。 现在有一个叫做对象解构的特性来访问嵌套对象。

 const data = { code: 42, items: [{ id: 1, name: 'foo' }, { id: 2, name: 'bar' }] }; const { items: [, { name: secondName }] } = data; console.log(secondName); 

使用JSONPath将是最灵活的解决scheme之一,如果你愿意包含一个库: https : //github.com/s3u/JSONPath (节点和浏览器)

对于你的用例,jsonpath是:

 $..items[1].name 

所以:

 var secondName = jsonPath.eval(data, "$..items[1].name"); 

我更喜欢JQuery。 它更清洁,易于阅读。

  $.each($.parseJSON(data), function (key, value) { alert(value.<propertyname>); }); 

如果您正在查找符合特定条件的一个或多个对象,则使用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:""}); 

还有一个singlesingleOrDefault他们分别像firstfirstOrDefault一样工作。 唯一的区别是,如果发现多于一个的匹配,他们将抛出。

为了进一步解释查询-js,你可以从这篇文章开始

下划线的方式

这是一个JavaScript库,它提供了一大堆有用的functional programming助手,而不需要扩展任何内置对象。

解:

 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); } 

一个pythonic,recursion和function的方法来解开任意的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) 

其中数据是一个Python列表(从JSON文本stringparsing):

 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']} ] 

我不认为提问者只关心一个层次的嵌套对象,所以我给出下面的演示来演示如何访问深度嵌套的json对象的节点。 好吧,让我们findID为“5”的节点。

 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)); } }); 
 <script src="dabeng/JSON-Loop/master/JSONLoop.js"></script> 

老问题,但没有人提到lodash(只是下划线)。

如果你已经在你的项目中使用lodash,我想在一个复杂的例子中做一个很好的方法:

select1

 _.get(response, ['output', 'fund', 'data', '0', 'children', '0', 'group', 'myValue'], '') 

与…一样:

select2

 response.output.fund.data[0].children[0].group.myValue 

第一个和第二个选项之间的差异是,在Opt 1中,如果在path中缺less某个属性(未定义),则不会收到错误,它将返回第三个参数。

对于数组过滤lodash有_.find()但我宁愿使用常规filter() 。 但是我仍然认为上面的方法_.get()在处理真正复杂的数据时是非常有用的。 我在过去遇到过非常复杂的API,而且非常方便!

我希望对于谁在寻找操作标题所暗示的真正复杂的数据的选项是有用的。

你可以使用lodash _get函数:

 var object = { 'a': [{ 'b': { 'c': 3 } }] }; _.get(object, 'a[0].b.c'); // => 3 

jQuery的grep函数可以让你过滤一个数组:

 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"} 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 

使用lodash将是很好的解决scheme

例如:

 var object = { 'a': { 'b': { 'c': 3 } } }; _.get(object, 'abc'); // => 3