EDIT : Tous les exemples ci-dessous dans cette réponse ont été modifiés pour inclure une nouvelle variable de chemin fournie par l'itérateur conformément à la demande de @ supersan . La variable path est un tableau de chaînes où chaque chaîne du tableau représente chaque clé à laquelle on a accédé pour accéder à la valeur itérée résultante à partir de l'objet source d'origine. La variable path peut être introduite dans la fonction / méthode get de lodash . Ou vous pouvez écrire votre propre version de get de lodash qui ne gère que les tableaux comme ceci:
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"]));
EDIT : Cette réponse modifiée résout des traversées en boucle infinies.
Arrêter les traversées d'objets infinis
Cette réponse modifiée fournit toujours l'un des avantages supplémentaires de ma réponse originale qui vous permet d'utiliser la fonction de générateur fournie afin d'utiliser une interface itérable plus propre et simple (pensez à utiliser des for of
boucles comme dans for(var a of b)
où b
est un itérable et a
est un élément de l'itérable ). En utilisant la fonction de générateur en plus d'être une API plus simple, cela aide également à la réutilisation du code en évitant d'avoir à répéter la logique d'itération partout où vous souhaitez itérer en profondeur sur les propriétés d'un objet et cela permet également debreak
sortir de la boucle si vous souhaitez arrêter l'itération plus tôt.
Une chose que je remarque et qui n'a pas été abordée et qui n'est pas dans ma réponse originale est que vous devez être prudent en parcourant des objets arbitraires (c'est-à-dire tout ensemble "aléatoire"), car les objets JavaScript peuvent être auto-référencés. Cela crée la possibilité d'avoir des traversées en boucle infinies. Les données JSON non modifiées ne peuvent cependant pas être auto-référencées, donc si vous utilisez ce sous-ensemble particulier d'objets JS, vous n'avez pas à vous soucier des traversées en boucle infinies et vous pouvez vous référer à ma réponse originale ou à d'autres réponses. Voici un exemple de parcours sans fin (notez que ce n'est pas un morceau de code exécutable, car sinon, il planterait l'onglet de votre navigateur).
Également dans l'objet générateur de mon exemple édité, j'ai choisi d'utiliser à la Object.keys
place de for in
laquelle n'itère que des clés non prototypes sur l'objet. Vous pouvez l'échanger vous-même si vous souhaitez que les clés prototypes soient incluses. Voir ma section de réponse originale ci-dessous pour les deux implémentations avec Object.keys
et for in
.
Pire - Cela fera une boucle infinie sur les objets auto-référentiels:
//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);
}
Pour vous sauver de cela, vous pouvez ajouter un ensemble dans une fermeture, de sorte que lorsque la fonction est appelée pour la première fois, elle commence à créer une mémoire des objets qu'elle a vus et ne continue pas l'itération une fois qu'elle rencontre un objet déjà vu. L'extrait de code ci-dessous fait cela et gère donc des cas de boucle infinis.
Mieux - Ce ne sera pas une boucle infinie sur les objets auto-référentiels:
//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);
}
Réponse originale
Pour une nouvelle façon de le faire si cela ne vous dérange pas d'abandonner IE et de prendre en charge principalement les navigateurs plus récents (consultez le tableau es6 de kangax pour la compatibilité). Vous pouvez utiliser les générateurs es2015 pour cela. J'ai mis à jour la réponse de @ TheHippo en conséquence. Bien sûr, si vous voulez vraiment le support IE, vous pouvez utiliser le transpilateur 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);
}
Si vous ne voulez que des propriétés énumérables propres (essentiellement des propriétés de chaîne non prototypes), vous pouvez le modifier pour itérer en utilisant Object.keys
et une for...of
boucle à la place:
//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);
}