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

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

Например:

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

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

Ответ 1

Отборочные

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

(Plain) Объекты имеют вид

{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, и поэтому рекомендуется прочитать Руководство по JavaScript MDN, особенно разделы



Доступ к вложенным структурам данных

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

Вот пример:

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...in с 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 для итерации по массивам, но есть причины, по которым этого следует избегать: Почему "для (элемент var в списке)" с массивами, считающимися плохими практика в 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), вы также можете использовать цикл for...of [MDN], который не только работает для массивов, но и для любого iterable:

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

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


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

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

Но если структура данных содержит повторяющиеся шаблоны, например. представление двоичного дерева, решение обычно включает в себя рекурсивно [Wikipedia] доступ каждый уровень структуры данных.

Вот пример, чтобы получить первый лист node двоичного дерева:

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

Ответ 2

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

data.items[1].name

или

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

Оба способа равны.

Ответ 3

Если вы пытаетесь получить доступ к 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 цента.

Ответ 4

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

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

Но это часто бывает не со сложным json. По мере усложнения json подходы к определению значений внутри json также становятся сложными. Рекурсивный подход для навигации по json лучше всего, и то, как эта рекурсия используется, будет зависеть от типа поиска данных. Если есть условные заявления, json search может быть хорошим инструментом для использования.

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

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.

Ответ 5

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

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

Ответ 6

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

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

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

console.log(secondName);

Ответ 7

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

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

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.

Ответ 8

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

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

$..items[1].name

так:

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

Ответ 9

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

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

Ответ 10

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

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

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

Ответ 11

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

Ответ 12

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

Ответ 13

Подсвечник 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);
}

Ответ 14

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

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

Opt 1

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

то же самое, что:

Opt 2

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

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

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

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

Ответ 15

Я не думаю, что вопросник касается только одного вложенного объекта уровня, поэтому я представляю следующую демонстрацию, чтобы продемонстрировать, как получить доступ к node глубоко вложенного объекта json. Хорошо, найдем node с id '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>

Ответ 16

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

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/andreitodorut/3mws3kjL/

Ответ 17

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

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

1. Схема доступа вложенных объектов Oliver Steele

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

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

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

Невозможно прочитать имя свойства "undefined".

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

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

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

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.

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

Ответ 18

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

Ответ 19

Функция 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>

Ответ 20

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

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

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

Ответ 21

Здесь используется динамический подход - ваш "глубокий" ключ - это строковые 'items[1].name' (вы можете использовать обозначение массива [i] на любом уровне) - если ключ недействителен, возвращается undefined.

let deep = (o,k) => {
  return k.split('.').reduce((a,c,i) => {
    let m=c.match(/(.*?)\[(\d*)\]/);
    if(m && a!=null && a[m[1]]!=null) return a[m[1]][+m[2]];
    return a==null ? a: a[c];
  },o)
}

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

let deep = (o,k) => {
  return k.split('.').reduce((a,c,i) => {
    let m=c.match(/(.*?)\[(\d*)\]/);
    if(m && a!=null && a[m[1]]!=null) return a[m[1]][+m[2]];
    return a==null ? a: a[c];
  },o)
}

console.log( deep(data,'items[1].name') );

Ответ 22

Использование lodash было бы хорошим решением

Пример:

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