EDYCJA : Wszystkie poniższe przykłady w tej odpowiedzi zostały zmodyfikowane tak, aby zawierały nową zmienną ścieżki uzyskaną z iteratora zgodnie z żądaniem @ supersan . Zmienna ścieżki jest tablicą ciągów, w której każdy ciąg w tablicy reprezentuje każdy klucz, do którego uzyskano dostęp do wynikowej iterowanej wartości z oryginalnego obiektu źródłowego. Zmienna ścieżki może być wprowadzona do funkcji / metody get lodash . Lub możesz napisać własną wersję get lodash, która obsługuje tylko takie tablice:
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"]));
EDYTOWAĆ : ta zmieniona odpowiedź rozwiązuje nieskończone pętle przechodzenia.
Zatrzymywanie nieznośnego przechodzenia przez nieskończone obiekty
Ta zredagowana odpowiedź nadal zapewnia jedną z dodatkowych zalet mojej oryginalnej odpowiedzi, która pozwala na użycie dostarczonej funkcji generatora w celu użycia czystszego i prostego iterowalnego interfejsu (pomyśl o użyciu for ofpętli tak, jak w for(var a of b)przypadku, gdy bjest iterowalny i ajest elementem iterowalnym ). Używając funkcji generatora, będąc prostszym interfejsem API, pomaga również w ponownym użyciu kodu, dzięki czemu nie musisz powtarzać logiki iteracji wszędzie tam, gdzie chcesz głęboko iterować właściwości obiektu, a także umożliwia breakwyjście z pętla, jeśli chcesz wcześniej przerwać iterację.
Zauważyłem, że jedna rzecz, która nie została poruszona i której nie ma w mojej pierwotnej odpowiedzi, to ostrożność podczas przechodzenia przez dowolne (tj. Dowolny "losowy" zestaw) obiektów, ponieważ obiekty JavaScript mogą odwoływać się do siebie. Stwarza to możliwość nieskończonej pętli przechodzenia. Niezmodyfikowane dane JSON nie mogą jednak odwoływać się do siebie, więc jeśli używasz tego szczególnego podzbioru obiektów JS, nie musisz się martwić o nieskończone pętle przechodzenia i możesz odwołać się do mojej oryginalnej odpowiedzi lub innych odpowiedzi. Oto przykład niekończącego się przejścia (pamiętaj, że nie jest to fragment kodu, który można uruchomić, ponieważ w przeciwnym razie spowodowałoby to awarię karty przeglądarki).
Również w obiekcie generatora w moim edytowanym przykładzie zdecydowałem się użyć, Object.keyszamiast for inktórego iteruje tylko klucze nieprototypowe na obiekcie. Możesz to zmienić samodzielnie, jeśli chcesz, aby zawierały klucze prototypowe. Zobacz moją oryginalną sekcję odpowiedzi poniżej dla obu implementacji z Object.keysifor in .
Gorzej - spowoduje to nieskończoną pętlę na obiektach odnoszących się do samych siebie:
//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);
}
Aby się przed tym uchronić, możesz dodać zestaw w zamknięciu, tak aby przy pierwszym wywołaniu funkcji zaczęła budować pamięć obiektów, które widziała i nie kontynuowała iteracji, gdy napotka już widziany obiekt. Poniższy fragment kodu robi to, a zatem obsługuje nieskończone przypadki zapętlenia.
Lepiej - to nie będzie nieskończonej pętli na obiektach odwołujących się do siebie:
//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);
}
Oryginalna odpowiedź
Aby uzyskać nowszy sposób, możesz to zrobić, jeśli nie masz nic przeciwko porzuceniu IE i głównie obsłudze nowszych przeglądarek (sprawdź tabelę es6 kangax pod kątem kompatybilności). Możesz użyć do tego generatorów es2015 . Odpowiednio zaktualizowałem odpowiedź @ TheHippo. Oczywiście, jeśli naprawdę chcesz obsługiwać IE, możesz użyć transpilera JavaScript babel .
//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);
}
Jeśli chcesz mieć tylko własne wyliczalne właściwości (w zasadzie nie-prototypowe właściwości łańcucha), możesz zmienić to na iterację przy użyciu Object.keysi for...ofzamiast tego pętli:
//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);
}