如何访问和处理嵌套对象,数组或JSON?

本文翻译自:How can I access and process nested objects, arrays or JSON?

I have a nested data structure containing objects and arrays. 我有一个包含对象和数组的嵌套数据结构。 How can I extract the information, ie access a specific or multiple values (or keys)? 如何提取信息,即访问特定或多个值(或键)?

For example: 例如:

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

How could I access the name of the second item in items ? 我怎么能访问name中的第二项的items


#1楼

参考:https://stackoom.com/question/o1YV/如何访问和处理嵌套对象-数组或JSON


#2楼

Preliminaries 初赛

JavaScript has only one data type which can contain multiple values: Object . JavaScript只有一种数据类型,可以包含多个值: Object An Array is a special form of object. 数组是对象的一种特殊形式。

(Plain) Objects have the form (普通)对象具有以下形式

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

Arrays have the form 数组具有以下形式

[value, value, ...]

Both arrays and objects expose a key -> value structure. 数组和对象都公开key -> value结构。 Keys in an array must be numeric, whereas any string can be used as key in objects. 数组中的键必须是数字,而任何字符串都可以用作对象中的键。 The key-value pairs are also called the "properties" . 键值对也称为“属性”

Properties can be accessed either using dot notation 可以使用点表示法来访问属性

const value = obj.someProperty;

or bracket notation , if the property name would not be a valid JavaScript identifier name [spec] , or the name is the value of a variable: 括号表示法 ,如果属性名称不是有效的JavaScript 标识符名称[spec] ,或者名称是变量的值:

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

For that reason, array elements can only be accessed using bracket notation: 因此,只能使用方括号表示法访问数组元素:

const value = arr[5]; // arr.5 would be a syntax error

// property name / index as variable
const x = 5;
const value = arr[x];

Wait... what about JSON? 等等... JSON呢?

JSON is a textual representation of data, just like XML, YAML, CSV, and others. JSON是数据的文本表示形式,就像XML,YAML,CSV和其他形式一样。 To work with such data, it first has to be converted to JavaScript data types, ie arrays and objects (and how to work with those was just explained). 要使用此类数据,首先必须将其转换为JavaScript数据类型,即数组和对象(以及如何使用它们进行了说明)。 How to parse JSON is explained in the question Parse JSON in JavaScript? 如何在JavaScript中解析JSON问题中说明了如何解析JSON .

Further reading material 进一步阅读材料

How to access arrays and objects is fundamental JavaScript knowledge and therefore it is advisable to read the MDN JavaScript Guide , especially the sections 如何访问数组和对象是JavaScript的基本知识,因此建议阅读《 MDN JavaScript指南》 ,尤其是本节中的内容。



Accessing nested data structures 访问嵌套数据结构

A nested data structure is an array or object which refers to other arrays or objects, ie its values are arrays or objects. 嵌套数据结构是引用其他数组或对象的数组或对象,即其值是数组或对象。 Such structures can be accessed by consecutively applying dot or bracket notation. 可以通过连续应用点或括号符号来访问此类结构。

Here is an example: 这是一个例子:

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

Let's assume we want to access the name of the second item. 假设我们要访问第二项的name

Here is how we can do it step-by-step: 这是我们逐步执行的方法:

As we can see data is an object, hence we can access its properties using dot notation. 如我们所见, data是一个对象,因此我们可以使用点表示法访问其属性。 The items property is accessed as follows: items属性的访问方式如下:

data.items

The value is an array, to access its second element, we have to use bracket notation: 该值是一个数组,要访问其第二个元素,我们必须使用括号表示法:

data.items[1]

This value is an object and we use dot notation again to access the name property. 该值是一个对象,我们再次使用点符号来访问name属性。 So we eventually get: 因此,我们最终得到:

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

Alternatively, we could have used bracket notation for any of the properties, especially if the name contained characters that would have made it invalid for dot notation usage: 或者,我们可以对任何属性使用括号符号,特别是如果名称包含使点符号用法无效的字符时:

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

I'm trying to access a property but I get only undefined back? 我正在尝试访问属性,但只得到undefined返回?

Most of the time when you are getting undefined , the object/array simply doesn't have a property with that name. 在大多数情况下,当您undefined ,对象/数组根本没有具有该名称的属性。

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

Use console.log or console.dir and inspect the structure of object / array. 使用console.logconsole.dir并检查对象/数组的结构。 The property you are trying to access might be actually defined on a nested object / array. 您尝试访问的属性可能实际上是在嵌套对象/数组上定义的。

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

What if the property names are dynamic and I don't know them beforehand? 如果属性名称是动态的,但我事先不知道该怎么办?

If the property names are unknown or we want to access all properties of an object / elements of an array, we can use the for...in [MDN] loop for objects and the for [MDN] loop for arrays to iterate over all properties / elements. 如果属性名称是未知的,或者我们要访问对象的所有属性/数组元素,我们可以使用for...in [MDN]环为对象和for [MDN]环对数组遍历所有属性/元素。

Objects 对象

To iterate over all properties of data , we can iterate over the object like so: 要遍历data所有属性,我们可以像这样遍历对象

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
}

Depending on where the object comes from (and what you want to do), you might have to test in each iteration whether the property is really a property of the object, or it is an inherited property. 根据对象的来源(以及您要执行的操作),您可能必须在每次迭代中测试该属性是否确实是该对象的属性,还是它是继承的属性。 You can do this with Object#hasOwnProperty [MDN] . 您可以使用Object#hasOwnProperty [MDN]进行此操作

As alternative to for...in with hasOwnProperty , you can use Object.keys [MDN] to get an array of property names : for...in hasOwnProperty ,作为for...in替代方法,可以使用Object.keys [MDN]获取属性名称数组

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

Arrays 数组

To iterate over all elements of the data.items array , we use a for loop: 要遍历data.items 数组的所有元素,我们使用一个for循环:

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`.
}

One could also use for...in to iterate over arrays, but there are reasons why this should be avoided: Why is 'for(var item in list)' with arrays considered bad practice in JavaScript? 人们也可以使用for...in遍历数组,但是有理由应避免这种情况: 为什么在JavaScript中将数组的'for(var item in list)'视为不好的做法? .

With the increasing browser support of ECMAScript 5, the array method forEach [MDN] becomes an interesting alternative as well: 与浏览器支持的ECMAScript 5的增大,阵列方法forEach [MDN]成为一个有趣的选择,以及:

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 (i.e. `data.items` in this case)
}); 

In environments supporting ES2015 (ES6), you can also use the for...of [MDN] loop, which not only works for arrays, but for any iterable : 在支持ES2015(ES6)的环境中,您还可以使用for...of [MDN]循环,该循环不仅适用于数组,而且适用于任何可迭代的方法

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

In each iteration, for...of directly gives us the next element of the iterable, there is no "index" to access or use. 在每次迭代中, for...of直接为我们提供了可迭代的下一个元素,没有“索引”可供访问或使用。


What if the "depth" of the data structure is unknown to me? 如果我不知道数据结构的“深度”怎么办?

In addition to unknown keys, the "depth" of the data structure (ie how many nested objects) it has, might be unknown as well. 除了未知键之外,数据结构的“深度”(即,有多少个嵌套对象)也可能是未知的。 How to access deeply nested properties usually depends on the exact data structure. 如何访问深层嵌套的属性通常取决于确切的数据结构。

But if the data structure contains repeating patterns, eg the representation of a binary tree, the solution typically includes to recursively [Wikipedia] access each level of the data structure. 但是,如果数据结构包含重复模式,例如二叉树的表示形式,则解决方案通常包括递归 [Wikipedia]访问数据结构的每个级别。

Here is an example to get the first leaf node of a binary tree: 这是获取二叉树的第一个叶子节点的示例:

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

A more generic way to access a nested data structure with unknown keys and depth is to test the type of the value and act accordingly. 访问具有未知键和深度的嵌套数据结构的一种更通用的方法是测试值的类型并采取相应的措施。

Here is an example which adds all primitive values inside a nested data structure into an array (assuming it does not contain any functions). 这是一个将嵌套数据结构内的所有原始值添加到数组中的示例(假设它不包含任何函数)。 If we encounter an object (or array) we simply call toArray again on that value (recursive call). 如果遇到一个对象(或数组),我们只需对该值再次调用toArray (递归调用)。

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

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



Helpers 帮手

Since the structure of a complex object or array is not necessarily obvious, we can inspect the value at each step to decide how to move further. 由于复杂对象或数组的结构不一定很明显,因此我们可以检查每一步的值以决定如何进一步移动。 console.log [MDN] and console.dir [MDN] help us doing this. console.log [MDN]console.dir [MDN]可以帮助我们做到这一点。 For example (output of the Chrome console): 例如(Chrome控制台的输出):

> console.log(data.items)
 [ Object, Object ]

Here we see that that data.items is an array with two elements which are both objects. 在这里,我们看到data.items是一个包含两个都是对象的元素的数组。 In Chrome console the objects can even be expanded and inspected immediately. 在Chrome控制台中,对象甚至可以立即展开和检查。

> console.log(data.items[1])
  Object
     id: 2
     name: "bar"
     __proto__: Object

This tells us that data.items[1] is an object, and after expanding it we see that it has three properties, id , name and __proto__ . 这告诉我们data.items[1]是一个对象,展开之后,我们看到它具有三个属性, idname__proto__ The latter is an internal property used for the prototype chain of the object. 后者是用于对象原型链的内部属性。 The prototype chain and inheritance is out of scope for this answer, though. 但是,原型链和继承超出了此答案的范围。


#3楼

You can access it this way 您可以这样访问

data.items[1].name

or 要么

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

Both ways are equal. 两种方法是平等的。


#4楼

In case you're trying to access an item from the example structure by id or name , without knowing it's position in the array, the easiest way to do it would be to use underscore.js library: 如果您试图通过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"}

From my experience, using higher order functions instead of for or for..in loops results in code that is easier to reason about, and hence more maintainable. 根据我的经验,使用高阶函数代替forfor..in循环会导致代码更易于推理,因此更易于维护。

Just my 2 cents. 只是我的2美分。


#5楼

Using JSONPath would be one of the most flexible solutions if you are willing to include a library: https://github.com/s3u/JSONPath (node and browser) 如果您愿意包含一个库,那么使用JSONPath将是最灵活的解决方案之一: https : //github.com/s3u/JSONPath (节点和浏览器)

For your use case the json path would be: 对于您的用例,json路径为:

$..items[1].name

so: 所以:

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

#6楼

At times, accessing a nested object using a string can be desirable. 有时,使用字符串访问嵌套对象可能是理想的。 The simple approach is the first level, for example 例如,简单的方法是第一级

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

But this is often not the case with complex json. 但这对于复杂的json通常不是这种情况。 As json becomes more complex, the approaches for finding values inside of the json also become complex. 随着json变得越来越复杂,在json内部查找值的方法也变得复杂。 A recursive approach for navigating the json is best, and how that recursion is leveraged will depend on the type of data being searched for. 导航json的一种递归方法是最好的,如何利用该递归将取决于要搜索的数据类型。 If there are conditional statements involved, a json search can be a good tool to use. 如果涉及条件语句,则使用json搜索可能是一个很好的工具。

If the property being accessed is already known, but the path is complex, for example in this object 如果所访问的属性是已知的,但是路径很复杂,例如在此对象中

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

And you know you want to get the first result of the array in the object, perhaps you would like to use 而且您知道要获取对象中数组的第一个结果,也许您想使用

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

However, that will cause an exception as there is no property of object with that name. 但是,这将导致异常,因为具有该名称的对象没有属性。 The solution to be able to use this would be to flatten the tree aspect of the object. 能够使用此方法的解决方案将是使对象的树宽展平。 This can be done recursively. 这可以递归完成。

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

Now, the complex object can be flattened 现在,可以将复杂对象展平

var obj = previous definition;
var flat = flatten(obj);
var moe = flat["arr[0].name"];//moe

Here is a jsFiddle Demo of this approach being used. 这是使用此方法的jsFiddle Demo

发布了0 篇原创文章 · 获赞 75 · 访问量 56万+

猜你喜欢

转载自blog.csdn.net/w36680130/article/details/105390721
今日推荐