Как я могу получить доступ и обработать вложенные объекты, массивы или JSON?

876

У меня есть вложенная структура данных, содержащая объекты и массивы. Как я могу извлечь информацию, то есть получить доступ к определенным или множественным значениям (или ключам)?

Например:

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

Как я могу получить доступ nameко второму пункту в items?

Феликс Клинг
источник
22
@Marcel: его нужно читать как «У меня есть структура данных с вложенными данными или JSON, как я могу получить доступ к определенному значению?». Я знаю разницу, но многие люди не ищут и могут искать «JSON», а не «объект». Многие вопросы на самом деле имеют форму «как я могу получить доступ к X в этом JSON». Единственное место, где я упоминаю JSON в своем ответе, - это то, где я объясняю, что это такое. Если у вас есть предложение, как лучше донести это, я весь в ушах.
Феликс Клинг
возможный дубликат JSON можно найти в JavaScript
Трэвис Дж

Ответы:

1161

прелиминарии

JavaScript имеет только один тип данных, который может содержать несколько значений: Object . Массив представляет собой особую форму объекта.

(Обычный) Объекты имеют вид

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

Массивы имеют форму

[value, value, ...]

И массивы, и объекты предоставляют key -> valueструктуру. Ключи в массиве должны быть числовыми, тогда как любая строка может использоваться в качестве ключа в объектах. Пары ключ-значение также называются «свойствами» .

Свойства могут быть доступны либо с использованием точечной нотации

const value = obj.someProperty;

или обозначение в скобках , если имя свойства не будет допустимым именем идентификатора 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];

По этой причине доступ к элементам массива возможен только в скобках:

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, то есть массивы и объекты (и как работать с ними только что объяснено). Как разобрать JSON объясняется в вопросе Parse JSON в JavaScript? ,

Дальнейшее чтение материала

Доступ к массивам и объектам является фундаментальным знанием JavaScript, и поэтому желательно прочитать руководство по MDN 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.logили console.dirи проверьте структуру объекта / массива. Свойство, к которому вы пытаетесь получить доступ, может быть фактически определено для вложенного объекта / массива.

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

Что если имена свойств являются динамическими, и я не знаю их заранее?

Если имена свойств неизвестны или мы хотим получить доступ ко всем свойствам объекта / элементов массива, мы можем использовать цикл for...in [MDN] для объектов и цикл for [MDN] для массивов для перебора всех свойств / элементов.

Объекты

Чтобы перебрать все свойства 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
}

В зависимости от того, откуда берется объект (и что вы хотите сделать), вам, возможно, придется проверять на каждой итерации, является ли свойство действительно свойством объекта или унаследованным свойством. Вы можете сделать это с Object#hasOwnProperty [MDN] .

В качестве альтернативы for...inwith hasOwnPropertyвы можете использовать 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`,..., 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`.
}

Можно также использовать for...inдля итерации массивов, но есть причины, по которым этого следует избегать: почему «for (var item in list)» с массивами считается плохой практикой в ​​JavaScript? ,

С увеличением поддержки браузером 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)
}); 

В средах, поддерживающих ES2015 (ES6), вы также можете использовать цикл [MDN] , который работает не только для массивов, но и для любых итераций :for...of

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

В каждой итерации, for...ofнепосредственно дает нам следующий элемент итерируемого, нет «индекса» для доступа или использования.


Что если «глубина» структуры данных мне неизвестна?

В дополнение к неизвестным ключам также может быть неизвестна «глубина» структуры данных (то есть, сколько вложенных объектов). Как получить доступ к глубоко вложенным свойствам, обычно зависит от точной структуры данных.

Но если структура данных содержит повторяющиеся узоры, например , представление двоичного дерева, то решение , как правило , включает в рекурсивно [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);

Более общий способ получить доступ к вложенной структуре данных с неизвестными ключами и глубиной - это проверить тип значения и действовать соответственно.

Вот пример, который добавляет все примитивные значения внутри вложенной структуры данных в массив (при условии, что он не содержит никаких функций). Если мы сталкиваемся с объектом (или массивом), мы просто 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;
}



Помощники

Поскольку структура сложного объекта или массива не обязательно очевидна, мы можем проверять значение на каждом шаге, чтобы решить, как двигаться дальше. console.log [MDN] и console.dir [MDN] помогают нам в этом. Например (вывод консоли Chrome):

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

Здесь мы видим, что data.itemsэто массив с двумя элементами, которые оба являются объектами. В консоли Chrome объекты могут быть даже расширены и проверены немедленно.

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

Это говорит нам о том, что data.items[1]это объект, и после его расширения мы видим, что у него есть три свойства id, nameи __proto__. Последнее является внутренним свойством, используемым для цепочки прототипов объекта. Тем не менее, цепочка прототипов и наследование выходят за рамки этого ответа.

Felix Kling
источник
3
Часть того, что здесь связано, действительно спрашивает, как это сделать в jQuery, что, если честно, упрощает одну или две вещи здесь. Не уверен, стоит ли сделать это больше мегапостом или ответить на него отдельно - основы, описанные здесь, о том, что является объектом, массивом, обычно являются тем, о чем действительно спрашивают ...
Крис Москини
1
@ felix-kling Одна вещь ... с вложенными объектами, например let object = {a: 1, b: 2, c: { a: 3, b: 4 }};, это возвращает массив, содержащий массив для каждого вложенного объекта, в этом случае [ 1, 2, [ 3, 4 ] ]не лучше ли использовать concat в рекурсивном вызове вместо push? (требующий, чтобы результат был изменчивым)
ElFitz
3
Это самый подробный ответ, который я когда-либо видел на Stack Overflow, и он ответил на мой вопрос! Спасибо!
Уильям Джонс
эта одна страница заставила меня узнать разницу между ARRAY и OBJ
4ni5
76

Вы можете получить к нему доступ таким образом

data.items[1].name

или

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

Оба пути равны.

vitmalina
источник
Да, но вы не можете делать данные ["items"]. 1.name
neaumusic
5
Первый намного более интуитивно понятен, удобочитаем и короче;) Я предпочитаю использовать синтаксис свойства скобок только тогда, когда имя свойства является переменным.
DanteTheSmith
35

В случае, если вы пытаетесь получить доступ itemк структуре примера с помощью idили name, не зная его положения в массиве, самый простой способ сделать это - использовать библиотеку 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"}

Исходя из моего опыта, использование функций более высокого порядка вместо циклов forили for..inприводит к коду, который легче рассуждать и, следовательно, более легок в обслуживании.

Просто мои 2 цента.

Голографический принцип,
источник
29

Объекты и массивы имеют много встроенных методов, которые могут помочь вам в обработке данных.

Примечание: во многих примерах я использую функции стрелок . Они похожи на выражения функций , но они связывают 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]]

Object.entries() с циклом for и деструктурирующим назначением

const obj = {
  a: 1
 ,b: 2
 ,c: 3
}

for (const [key, value] of Object.entries(obj)) {
  console.log(`key: ${key}, value: ${value}`)
}

Очень удобно итерировать результат Object.entries()с помощью цикла for и назначения деструктурирования .

Цикл For-of позволяет перебирать элементы массива. Синтаксис for (const element of array)(мы можем заменить constна varили let, но лучше использовать, constесли мы не собираемся изменять element).

Разрушающее присваивание позволяет извлекать значения из массива или объекта и назначать их переменным. В данном случае const [key, value]означает, что вместо присвоения [key, value]массива elementмы присваиваем первый элемент этого массива, keyа второй - value. Это эквивалентно этому:

for (const element of Object.entries(obj)) {
  const key = element[0]
       ,value = element[1]
}

Как видите, деструктуризация делает это намного проще.

Array.prototype.every() а также Array.prototype.some()

В every()метод возвращает значение, trueесли указанная функция обратного вызова возвращает trueдля каждого элемента массива. В some()метод возвращает значение, trueесли указанная функция обратного вызова возвращает trueдля некоторого элемента ( по крайней мере , одной).

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

Array.prototype.find() а также Array.prototype.filter()

Эти find()методы возвращают первый элемент , который удовлетворяет условия функции обратного вызова. filter()Метод возвращает массив всех элементов, удовлетворяющие условия функции обратного вызова.

const arr = [1, 2, 3]

// 2, because 2^2 !== 2
console.log(arr.find(x => x !== Math.pow(x, 2)))
// 1, because it's the first element
console.log(arr.find(x => true))
// undefined, because none of the elements equals 7
console.log(arr.find(x => x === 7))

// [2, 3], because these elements are greater than 1
console.log(arr.filter(x => x > 1))
// [1, 2, 3], because the function returns true for all elements
console.log(arr.filter(x => true))
// [], because none of the elements equals neither 6 nor 7
console.log(arr.filter(x => x === 6 || x === 7))

Array.prototype.map()

map()Метод возвращает массив с результатами вызова функции обратного вызова при условии на элементах массива.

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

Array.prototype.reduce()

reduce()Метод уменьшает массив к единственному значению, вызвав предоставленную функцию обратного вызова с двумя элементами.

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

reduce()Метод принимает необязательный второй параметр, который представляет собой первоначальное значение. Это полезно, когда массив, к которому вы reduce()обращаетесь, может содержать ноль или один элемент. Например, если мы хотим создать функцию, sum()которая принимает массив в качестве аргумента и возвращает сумму всех элементов, мы могли бы написать это так:

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

Михал Перлаковский
источник
Это мой любимый ответ. Вы также можете добавить пример для цикла только для конкретных вложенных данных, напримерObject.keys(data["items"]).forEach(function(key) { console.log(data["items"][key].id); console.log(data["items"][key].name); });
SilverSurfer
25

Иногда доступ к вложенному объекту с использованием строки может быть желательным. Простой подход - это, например, первый уровень

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 Demoэтот подход используется.

Трэвис Дж
источник
WTH вы бы хотели использовать obj["arr[0].name"]вместо obj.arr[0].name? Вам вряд ли нужно / нужно иметь дело со сплющенными объектами, за исключением сериализации.
Берги
@Bergi - я часто вижу этот вопрос, и поскольку он используется канонически, я опубликовал ответ на эту версию. Если этого можно избежать, то гораздо быстрее использовать obj.arr [0] .name, но иногда люди хотят передавать строковые методы доступа, и это пример того, как это сделать.
Трэвис Дж
Urgh. Тем не менее, вряд ли есть причина, чтобы сгладить весь объект, используя только один строковый путь, вы можете просто проанализировать его и выполнить динамический поиск.
Берги
14

Этот вопрос довольно старый, так как современное обновление. С началом ES2015 есть альтернативы, чтобы получить данные, которые вам нужны. Теперь есть функция, называемая деструктуризацией объектов, для доступа к вложенным объектам.

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

const {
  items: [, {
    name: secondName
  }]
} = data;

console.log(secondName);

В приведенном выше примере создается переменная, вызываемая secondNameиз nameключа из массива, который называется itemsодинокий, ,пропустить первый объект в массиве.

Примечательно, что в этом примере это, вероятно, излишне, так как простой доступ к массиву легче читать, но он полезен при разделении объектов в целом.

Это очень краткое введение в ваш конкретный вариант использования, деструктуризация может быть необычным синтаксисом, к которому нужно сначала привыкнуть. Я бы порекомендовал прочитать документацию Mozilla's Destructuring Assignment, чтобы узнать больше.

Алекс Кейс Смит
источник
13

Чтобы получить доступ к вложенному атрибуту, необходимо указать его имя, а затем выполнить поиск по объекту.

Если вы уже знаете точный путь, вы можете жестко закодировать его в своем скрипте следующим образом:

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)

Путь - это способ сказать: сначала возьмите объект с ключом items, который оказывается массивом. Затем возьмите 1-st элемент (0 индексных массивов). Последний возьмите объект с ключом nameв этом элементе массива, который является строкой bar.

Если у вас очень длинный путь, вы можете даже использовать, String.splitчтобы сделать все это проще -

'items.1.name'.split('.').reduce((a,v) => a[v], data)

Это просто обычный JavaScript, без использования сторонних библиотек, таких как jQuery или lodash.

Евгений
источник
13
var ourStorage = {


"desk":    {
    "drawer": "stapler"
  },
"cabinet": {
    "top drawer": { 
      "folder1": "a file",
      "folder2": "secrets"
    },
    "bottom drawer": "soda"
  }
};
ourStorage.cabinet["top drawer"].folder2; // Outputs -> "secrets"

или

//parent.subParent.subsubParent["almost there"]["final property"]

В основном, используйте точку между каждым потомком, который разворачивается под ним, и когда у вас есть имена объектов, состоящие из двух строк, вы должны использовать нотацию ["obj Name"]. В противном случае достаточно точки;

Источник: https://learn.freecodecamp.org/javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects

добавим, что доступ к вложенным массивам будет происходить так:

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-arrays/

Еще один более полезный документ, описывающий ситуацию выше: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Basics#Bracket_notation

Доступ к собственности с помощью точечного хождения: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors#Dot_notation

Джонни
источник
Хотя эта ссылка может ответить на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если связанная страница изменится. - Из обзора
Роберт
1
Я редактировал пост. Несмотря на то, что люди быстро дали ему плохую репутацию. В следующий раз я воздержусь от ответа.
Джонни
1
@Riddick не воздерживайтесь, просто убедитесь, что вы не
публикуете
12

Вы можете использовать lodash _getфункцию:

var object = { 'a': [{ 'b': { 'c': 3 } }] };

_.get(object, 'a[0].b.c');
// => 3
Сергей
источник
9

Использование JSONPath будет одним из наиболее гибких решений, если вы захотите включить библиотеку: https://github.com/s3u/JSONPath (узел и браузер)

Для вашего случая использования путь json будет:

$..items[1].name

так:

var secondName = jsonPath.eval(data, "$..items[1].name");
Андрейс
источник
1
Использование eval () не является хорошим решением. Вместо этого можно использовать функцию первого класса.
Pradeep Gowda
8

На всякий случай, если кто-нибудь посетит этот вопрос в 2017 году или позже и ищет легкий для запоминания способ, вот подробный пост в блоге о доступе к вложенным объектам в JavaScript без обмана со стороны

Невозможно прочитать свойство 'foo' с неопределенной ошибкой

1. Шаблон доступа к вложенным объектам Оливера Стила

Самый простой и чистый способ - использовать шаблон доступа к вложенным объектам Оливера Стила.

const name = ((user || {}).personalInfo || {}).name;

С этим обозначением вы никогда не столкнетесь с

Невозможно прочитать свойство 'имя' из неопределенного .

Вы в основном проверяете, существует ли пользователь, если нет, вы создаете пустой объект на лету. Таким образом, ключ следующего уровня будет всегда доступен из существующего объекта или пустого объекта , но никогда из неопределенного.

2. Доступ к вложенным объектам с помощью Array Reduce

Чтобы иметь доступ к вложенным массивам, вы можете написать свой собственный массив execute.

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.

Существует также превосходная обработка типов минимальной библиотеки типов, которая сделает все это за вас.

Динеш Пандийян
источник
3
Этот вопрос в первую очередь о свойствах доступа, которые существуют. Уже есть вопрос о том, на что вы ссылаетесь (и уже включаете большинство ваших решений): безопасный доступ к вложенным объектам Javascript или доступ к вложенным объектам JavaScript с помощью строкового ключа . Но в любом случае: «К сожалению, вы не можете получить доступ к вложенным массивам с помощью этого трюка». Почему бы нет? Массивы являются объектами, поэтому они должны работать так же хорошо. Можете ли вы привести пример, где это не так?
Феликс Клинг
1
@FelixKling Когда мы пытаемся получить доступ к массивам с помощью шаблона Оливера Стила, мы не сможем на лету создать массив длины 'n' и получить доступ к n-му индексу без ошибки 'undefined'. Ex. ((user || {}).address || new Array(3))[1].name
Динеш Пандийян
3
Вы не применяете свой шаблон последовательно. Конечно, ...[1].barэто приведет к ошибке, если элемент 1не существует. Но это также касается случая, ....foo.barесли fooне существовало. Вы также должны «охранять» доступ 1, точно так же, как вы «охраняете» любой другой доступ к собственности. Массив - это просто объект. «Элемент массива» - это просто свойство. Правильно это будет применено (((user || {}).address || {})[1] || {}).name.
Феликс Клинг
1
Это замечательно. Это меня не поразило. Спасибо @FelixKling, я пойду обновлять сообщения в блоге.
Динеш Пандийян
2
@DineshPandiyan, вы должны сообщить, что вы автор Typy, я только что пришел сюда после прочтения вашего поста в блоге
reggaeguitar
8

Я предпочитаю JQuery. Это чище и легко читается.

$.each($.parseJSON(data), function (key, value) {
  alert(value.<propertyname>);
});
Руди Инохоса
источник
7

Доступ к динамически многоуровневому объекту.

var obj = {
  name: "john doe",
  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/andreitodorut/3mws3kjL/

Андрей Тодорут
источник
6

Если вы ищете один или несколько объектов, которые соответствуют определенным критериям, у вас есть несколько вариантов, используя 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они работают так же, как firstи firstOrDefaultсоответственно. Единственное отличие состоит в том , что они будут бросать , если более чем один матч найден.

для дальнейшего объяснения query-js вы можете начать с этого поста

Руна ФС
источник
Я хотел бы знать, как это можно улучшить. Хотите оставить комментарий?
Руна FS
6

Подчеркнуть JS Way

Это библиотека 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);
}
Мохан Дере
источник
6

Старый вопрос, но как никто не упомянул lodash (только подчеркивание).

Если вы уже используете lodash в своем проекте, я думаю, элегантный способ сделать это в сложном примере:

Опция 1

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

такой же как:

Опция 2

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

Разница между первым и вторым вариантом заключается в том, что в параметре 1, если у вас отсутствует одно из (отсутствующих) свойств в пути, вы не получите ошибку, он возвращает вам третий параметр.

Для фильтра массива lodash есть, _.find()но я бы лучше использовал обычный filter(). Но я все еще думаю, что вышеупомянутый метод _.get()очень полезен при работе с действительно сложными данными. В прошлом я сталкивался с действительно сложными API, и это было удобно!

Я надеюсь, что это может быть полезно для тех, кто ищет варианты для манипулирования действительно сложными данными, которые подразумевает заголовок.

Тиаго С. С Вентура
источник
5

Я не думаю, что спрашивающий касается только вложенного объекта одного уровня, поэтому я представляю следующую демонстрацию, чтобы продемонстрировать, как получить доступ к узлу глубоко вложенного объекта json. Хорошо, давайте найдем узел с идентификатором '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="https://rawgit.com/dabeng/JSON-Loop/master/JSONLoop.js"></script>

dabeng
источник
Как получить доступ к вложенному объекту json с помощью переменных. data = {a: {b: 'ss'}}; var ключ = ab данные [ключ] не работает
Pasupathi Rajamanickam
3

Вы можете использовать синтаксис jsonObject.keyдля доступа к значению. И если вы хотите получить доступ к значению из массива, вы можете использовать синтаксис jsonObjectArray[index].key.

Вот примеры кода для доступа к различным значениям, чтобы дать вам идею.

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

        // if you want 'bar'
        console.log(data.items[1].name);

        // if you want array of item names
        console.log(data.items.map(x => x.name));

        // get the id of the item where name = 'bar'
        console.log(data.items.filter(x => (x.name == "bar") ? x.id : null)[0].id);

Рахул Вала
источник
3

Динамический подход

В приведенной ниже deep(data,key)функции вы можете использовать произвольную keyстроку - в вашем случае items[1].name(вы можете использовать запись массива [i]на любом уровне) - если ключ недействителен, то возвращается undefined.

Камил Келчевски
источник
2

Питонический, рекурсивный и функциональный подход для раскрытия произвольных деревьев 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):

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']}
]
pX0r
источник
6
Этот вопрос касается JavaScript, а не Python. Не уверен, есть ли эквивалентный вопрос для Python.
Феликс Клинг
2

Функция grep в jQuery позволяет фильтровать массив:

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>


источник
2
// 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;
}
Майкл Диммитт
источник
2

В 2020 году вы можете использовать @ babel / plugin-Предложение-опционально-цепочки, это очень легко получить доступ к вложенным значениям в объекте.

 const obj = {
 foo: {
   bar: {
     baz: class {
   },
  },
 },
};

const baz = new obj?.foo?.bar?.baz(); // baz instance

const safe = new obj?.qux?.baz(); // undefined
const safe2 = new obj?.foo.bar.qux?.(); // undefined

https://babeljs.io/docs/en/babel-plugin-proposal-optional-chaining

https://github.com/tc39/proposal-optional-chaining

Rathore
источник
-4

Мой stringdataидет из файла PHP, но все же, я указываю здесь, в var. Когда я непосредственно беру свой JSON в objнего, ничего не покажет, поэтому я положил свой файл JSON как

var obj=JSON.parse(stringdata); так что после этого я получаю messageobj и показываю в окне предупреждения, затем я получаю dataмассив json и сохраняю его в одну переменную, ArrObjзатем я читаю первый объект этого массива со значением ключа, как этоArrObj[0].id

     var stringdata={
        "success": true,
        "message": "working",
        "data": [{
                  "id": 1,
                  "name": "foo"
         }]
      };

                var obj=JSON.parse(stringdata);
                var key = "message";
                alert(obj[key]);
                var keyobj = "data";
                var ArrObj =obj[keyobj];

                alert(ArrObj[0].id);
Мантан Патель
источник
2
Пример сбивает с толку, потому что stringjsonэто не строка.
Феликс Клинг