РЕДАКТИРОВАТЬ : Все ответы были отредактированы и теперь содержат новую переменную пути, полученную из итератора, согласно запросу @ supersan . Переменная path - это массив строк, где каждая строка в массиве представляет каждый ключ, к которому был получен доступ, чтобы получить результирующее повторяющееся значение из исходного исходного объекта. Переменная пути может быть передана в функцию / метод lodash . Или вы можете написать свою собственную версию get lodash, которая обрабатывает только массивы, например:
function get (object, path) {
return path.reduce((obj, pathItem) => obj ? obj[pathItem] : undefined, object);
}
const example = {a: [1,2,3], b: 4, c: { d: ["foo"] }};
// these paths exist on the object
console.log(get(example, ["a", "0"]));
console.log(get(example, ["c", "d", "0"]));
console.log(get(example, ["b"]));
// these paths do not exist on the object
console.log(get(example, ["e", "f", "g"]));
console.log(get(example, ["b", "f", "g"]));
РЕДАКТИРОВАТЬ : Этот отредактированный ответ решает бесконечные циклические обходы.
Прекращение досадных обходов бесконечных объектов
Этот отредактированный ответ все еще предоставляет одно из дополнительных преимуществ моего первоначального ответа, который позволяет вам использовать предоставляемую функцию генератора для использования более чистого и простого итерируемого интерфейса (подумайте использование циклов for of
, как в for(var a of b)
, где b
- итерация, а a
- элемент итерации). Использование функции генератора наряду с тем, что он является более простым API, также помогает в повторном использовании кода, так как вам не нужно повторять логику итерации везде, где вы хотите глубоко перебрать свойства объекта, и это также позволяет break
вне цикла, если вы хотите остановить итерацию раньше.
Одна вещь, которую я заметил, что она не была рассмотрена и которой нет в моем первоначальном ответе, это то, что вы должны быть осторожны при обходе произвольных (то есть, любого «случайного» набора) объектов, потому что объекты JavaScript могут ссылаться на себя. Это создает возможность иметь бесконечные циклические обходы. Однако немодифицированные данные JSON не могут быть самореферентными, поэтому, если вы используете именно это подмножество объектов JS, вам не нужно беспокоиться о бесконечном цикле обходов и вы можете обратиться к моему исходному ответу или другим ответам. Вот пример бесконечного обхода (обратите внимание, что это не исполняемый фрагмент кода, потому что в противном случае он может вызвать сбой на вкладке браузера).
Также в объекте генератора в моем отредактированном примере я решил использовать Object.keys
вместо for in
, который выполняет итерации только не прототипных ключей объекта. Вы можете поменять это самостоятельно, если хотите, чтобы ключи прототипа были включены. См. Мой оригинальный раздел ответов ниже для обеих реализаций с Object.keys
и for in
.
Хуже - это будет бесконечный цикл для объектов со ссылками:
//your object
var o = {
foo:"bar",
arr:[1,2,3],
subo: {
foo2:"bar2"
}
};
// this self-referential property assignment is the only edited line
// from the below original example which makes the traversal
// non-terminating (i.e. it makes it infinite loop)
o.o = o;
function* traverse(o, path=[]) {
for (var i of Object.keys(o)) {
const itemPath = path.concat(i);
yield [i,o[i],itemPath];
if (o[i] !== null && typeof(o[i])=="object") {
//going one step down in the object tree!!
yield* traverse(o[I], itemPath);
}
}
}
//that's all... no magic, no bloated framework
for(var [key, value, path] of traverse(o)) {
// do something here with each key and value
console.log(key, value, path);
}
Чтобы уберечься от этого, вы можете добавить набор в замыкание, чтобы при первом вызове функции он начал формировать память из объектов, которые он видел, и не продолжал итерацию, когда натолкнулся на уже увиденный объект. , Приведенный ниже фрагмент кода делает это и таким образом обрабатывает бесконечные циклы.
Лучше - это не будет бесконечным циклом на объектах со ссылками:
//your object
var o = {
foo:"bar",
arr:[1,2,3],
subo: {
foo2:"bar2"
}
};
// this self-referential property assignment is the only edited line
// from the below original example which makes more naive traversals
// non-terminating (i.e. it makes it infinite loop)
o.o = o;
function* traverse(o) {
const memory = new Set();
function * innerTraversal (o, path=[]) {
if(memory.has(o)) {
// we've seen this object before don't iterate it
return;
}
// add the new object to our memory.
memory.add(o);
for (var i of Object.keys(o)) {
const itemPath = path.concat(i);
yield [i,o[i],itemPath];
if (o[i] !== null && typeof(o[i])=="object") {
//going one step down in the object tree!!
yield* innerTraversal(o[i], itemPath);
}
}
}
yield* innerTraversal(o);
}
console.log(o);
//that's all... no magic, no bloated framework
for(var [key, value, path] of traverse(o)) {
// do something here with each key and value
console.log(key, value, path);
}
Оригинальный ответ
Для более нового способа сделать это, если вы не против отказаться от IE и, в основном, поддерживать более современные браузеры (проверьте таблицу es6 kangax на совместимость). Для этого вы можете использовать генераторы es2015 . Я обновил ответ @ TheHippo соответственно. Конечно, если вы действительно нуждаетесь в поддержке IE, вы можете использовать babel JavaScript транспортер.
//your object
var o = {
foo:"bar",
arr:[1,2,3],
subo: {
foo2:"bar2"
}
};
function* traverse(o, path=[]) {
for (var i in o) {
const itemPath = path.concat(i);
yield [i,o[i],itemPath];
if (o[i] !== null && typeof(o[i])=="object") {
//going one step down in the object tree!!
yield* traverse(o[i], itemPath);
}
}
}
//that's all... no magic, no bloated framework
for(var [key, value, path] of traverse(o)) {
// do something here with each key and value
console.log(key, value, path);
}
Если вам нужны только собственные перечислимые свойства (в основном свойства цепочек, не являющиеся прототипами), вы можете изменить его на итерацию, используя Object.keys
и цикл for...of
вместо:
//your object
var o = {
foo:"bar",
arr:[1,2,3],
subo: {
foo2:"bar2"
}
};
function* traverse(o,path=[]) {
for (var i of Object.keys(o)) {
const itemPath = path.concat(i);
yield [i,o[i],itemPath];
if (o[i] !== null && typeof(o[i])=="object") {
//going one step down in the object tree!!
yield* traverse(o[i],itemPath);
}
}
}
//that's all... no magic, no bloated framework
for(var [key, value, path] of traverse(o)) {
// do something here with each key and value
console.log(key, value, path);
}