In loop ricorsivo attraverso un object per build un elenco di proprietà

Situazione: ho un object grande contenente più oggetti sub e sub-sub, con proprietà contenenti più tipi di dati. Per i nostri scopi, questo object ha un aspetto simile al seguente:

var object = { aProperty: { aSetting1: 1, aSetting2: 2, aSetting3: 3, aSetting4: 4, aSetting5: 5 }, bProperty: { bSetting1: { bPropertySubSetting : true }, bSetting2: "bString" }, cProperty: { cSetting: "cString" } } 

Ho bisogno di scorrere questo object e creare un elenco delle chiavi che mostrano la gerarchia, quindi la lista si presenta così:

 aProperty.aSetting1 aProperty.aSetting2 aProperty.aSetting3 aProperty.aSetting4 aProperty.aSetting5 bProperty.bSetting1.bPropertySubSetting bProperty.bSetting2 cProperty.cSetting 

Ho questa funzione, che scorre l’object e sputa le chiavi, ma non gerarchicamente:

 function iterate(obj) { for (var property in obj) { if (obj.hasOwnProperty(property)) { if (typeof obj[property] == "object") { iterate(obj[property]); } else { console.log(property + " " + obj[property]); } } } } 

Qualcuno può farmi sapere come farlo? Ecco un jsfiddle con cui fare casino: http://jsfiddle.net/tbynA/

Ho fatto un FIDDLE per te. Sto memorizzando una stringa di stack e quindi l’output, se la proprietà è di tipo primitivo:

 function iterate(obj, stack) { for (var property in obj) { if (obj.hasOwnProperty(property)) { if (typeof obj[property] == "object") { iterate(obj[property], stack + '.' + property); } else { console.log(property + " " + obj[property]); $('#output').append($("
").text(stack + '.' + property)) } } } } iterate(object, '')

AGGIORNAMENTO (24/07/2017)

Recentemente ho ricevuto un sacco di voti per questa domanda, quindi ho deciso di affinare la soluzione con un po ‘di magia ES2015 + e uno stile più funzionale.

Potrebbe essere meno leggibile, ma mi piace come sembra 🙂 È ancora ansible utilizzare una soluzione più semplice dall’alto – entrambi dovrebbero funzionare esattamente allo stesso modo.

 const isObject = val => typeof val === 'object' && !Array.isArray(val); const paths = (obj = {}) => Object.entries(obj) .reduce( (product, [key, value]) => isObject(value) ? product.concat([ [key, paths(value)] // adds [root, [children]] list ]) : product.concat([key]), // adds [child] list [] ) const addDelimiter = (a, b) => a ? `${a}.${b}` : b; const pathToString = ([root, children]) => children.map( child => Array.isArray(child) ? addDelimiter(root, pathToString(child)) : addDelimiter(root, child) ) .join('\n'); const input = { aProperty: { aSetting1: 1, aSetting2: 2, aSetting3: 3, aSetting4: 4, aSetting5: 5 }, bProperty: { bSetting1: { bPropertySubSetting: true }, bSetting2: "bString" }, cProperty: { cSetting: "cString" } }; // ^ implies a "root" level will be ["", paths(input)] // ideally paths() should return that structure, but I could not figure out how :) // shows desired output format console.log(pathToString(["", paths(input)])); // showcase the resulting data structure // any object can be recursively represented as a list [objectPropertyName, [...nestedPropertyNames]] // console.log(paths(input)); 

Incontrerai problemi con questo se l’object ha un loop nel suo object grafico, ad es. Qualcosa del tipo:

 var object = { aProperty: { aSetting1: 1 }, }; object.ref = object; 

In tal caso, potresti voler mantenere riferimenti di oggetti che hai già attraversato ed escluderli dall’iterazione.

Inoltre, è ansible riscontrare un problema se il grafico dell’object è troppo profondo come:

 var object = { a: { b: { c: { ... }} } }; 

Otterrai troppi errori di chiamata ricorsiva. Entrambi possono essere evitati:

 function iterate(obj) { var walked = []; var stack = [{obj: obj, stack: ''}]; while(stack.length > 0) { var item = stack.pop(); var obj = item.obj; for (var property in obj) { if (obj.hasOwnProperty(property)) { if (typeof obj[property] == "object") { var alreadyFound = false; for(var i = 0; i < walked.length; i++) { if (walked[i] === obj[property]) { alreadyFound = true; break; } } if (!alreadyFound) { walked.push(obj[property]); stack.push({obj: obj[property], stack: item.stack + '.' + property}); } } else { console.log(item.stack + '.' + property + "=" + obj[property]); } } } } } iterate(object); 

https://github.com/hughsk/flat

 var flatten = require('flat') flatten({ key1: { keyA: 'valueI' }, key2: { keyB: 'valueII' }, key3: { a: { b: { c: 2 } } } }) // { // 'key1.keyA': 'valueI', // 'key2.keyB': 'valueII', // 'key3.abc': 2 // } 

Solo loop per ottenere gli indici dopo.

Non hai bisogno di ricorsione!

La seguente funzione di funzione rapida che emetterà le voci nell’ordine dal minimo al più profondo con il valore della chiave come matrice [key, value] .

 function deepEntries( obj ){ 'use-strict'; var allkeys, curKey = '[', len = 0, i = -1, entryK; function formatKeys( entries ){ entryK = entries.length; len += entries.length; while (entryK--) entries[entryK][0] = curKey+JSON.stringify(entries[entryK][0])+']'; return entries; } allkeys = formatKeys( Object.entries(obj) ); while (++i !== len) if (typeof allkeys[i][1] === 'object' && allkeys[i][1] !== null){ curKey = allkeys[i][0] + '['; Array.prototype.push.apply( allkeys, formatKeys( Object.entries(allkeys[i][1]) ) ); } return allkeys; } 

Quindi, per produrre il tipo di risultati che stai cercando, basta usare questo.

 function stringifyEntries(allkeys){ return allkeys.reduce(function(acc, x){ return acc+((acc&&'\n')+x[0]) }, ''); }; 

Se sei interessato ai pezzi tecnici, allora è così che funziona. Funziona ottenendo Object.entries dell’object obj che hai passato e li mette in array allkeys . Quindi, passando allkeys di allkeys alla fine, se trova che uno dei allkeys di allkeys voci di allkeys è un object, ottiene la chiave di curKey come curKey e curKey ciascuna delle proprie chiavi di immissione con curKey prima che spinga l’array risultante su la fine di tutti i allkeys . Quindi, aggiunge il numero di voci aggiunte a tutti i allkeys alla lunghezza di destinazione in modo che anche le chiavi aggiunte di nuovo allkeys a allkeys .

Ad esempio, osserva quanto segue:

  

AGGIORNARE: SOLO USARE JSON.stringify per stampare oggetti sullo schermo!

Tutto ciò di cui hai bisogno è questa linea:

 document.body.innerHTML = '
' + JSON.stringify(ObjectWithSubObjects, null, "\t") + '

';

Questa è la mia versione precedente di stampa di oggetti ricorsivamente sullo schermo:

  var previousStack = ''; var output = ''; function objToString(obj, stack) { for (var property in obj) { var tab = '    '; if (obj.hasOwnProperty(property)) { if (typeof obj[property] === 'object' && typeof stack === 'undefined') { config = objToString(obj[property], property); } else { if (typeof stack !== 'undefined' && stack !== null && stack === previousStack) { output = output.substring(0, output.length - 1); // remove last } output += tab + '' + property + ': ' + obj[property] + '
'; // insert property output += '}'; // add last } again } else { if (typeof stack !== 'undefined') { output += stack + ': {
' + tab; } output += '' + property + ': ' + obj[property] + '
'; if (typeof stack !== 'undefined') { output += '}'; } } previousStack = stack; } } } return output; }

Uso:

 document.body.innerHTML = objToString(ObjectWithSubObjects); 

Esempio di output:

 cache: false position: fixed effect: { fade: false fall: true } 

Ovviamente questo può essere migliorato aggiungendo una virgola quando necessario e citazioni dai valori di stringa. Ma questo è stato abbastanza buono per il mio caso.

Una soluzione migliorata con possibilità di filtraggio. Questo risultato è più conveniente in quanto è ansible fare riferimento a qualsiasi proprietà dell’object direttamente con percorsi di array come:

[“aProperty.aSetting1”, “aProperty.aSetting2”, “aProperty.aSetting3”, “aProperty.aSetting4”, “aProperty.aSetting5”, “bProperty.bSetting1.bPropertySubSetting”, “bProperty.bSetting2”, “cProperty.cSetting” ]

  /** * Recursively searches for properties in a given object. * Ignores possible prototype endless enclosures. * Can list either all properties or filtered by key name. * * @param {Object} object Object with properties. * @param {String} key Property key name to search for. Empty string to * get all properties list . * @returns {String} Paths to properties from object root. */ function getPropertiesByKey(object, key) { var paths = [ ]; iterate( object, ""); return paths; /** * Single object iteration. Accumulates to an outer 'paths' array. */ function iterate(object, path) { var chainedPath; for (var property in object) { if (object.hasOwnProperty(property)) { chainedPath = path.length > 0 ? path + "." + property : path + property; if (typeof object[property] == "object") { iterate( object[property], chainedPath, chainedPath); } else if ( property === key || key.length === 0) { paths.push( chainedPath); } } } return paths; } } 

Questa versione è racchiusa in una funzione che accetta un delimitatore personalizzato, filtra e restituisce un dizionario piatto:

 function flatten(source, delimiter, filter) { var result = {} ;(function flat(obj, stack) { Object.keys(obj).forEach(function(k) { var s = stack.concat([k]) var v = obj[k] if (filter && filter(k, v)) return if (typeof v === 'object') flat(v, s) else result[s.join(delimiter)] = v }) })(source, []) return result } 
 var obj = { a: 1, b: { c: 2 } } flatten(obj) // <- Object {a: 1, bc: 2} flatten(obj, '/') // <- Object {a: 1, b/c: 2} flatten(obj, '/', function(k, v) { return k.startsWith('a') }) // <- Object {b/c: 2} 

Supponiamo di avere un object JSON come:

 var example = { "prop1": "value1", "prop2": [ "value2_0", "value2_1"], "prop3": { "prop3_1": "value3_1" } } 

Il modo sbagliato per scorrere le sue “proprietà”:

 function recursivelyIterateProperties(jsonObject) { for (var prop in Object.keys(jsonObject)) { console.log(prop); recursivelyIterateProperties(jsonObject[prop]); } } 

Potresti essere sorpreso di vedere la console che registra 0 , 1 , ecc. Durante l’iterazione attraverso le proprietà di prop1 e prop2 e di prop3_1 . Questi oggetti sono sequenze e gli indici di una sequenza sono proprietà di quell’object in Javascript.

Un modo migliore di ripetere iteramente in modo ricorsivo attraverso le proprietà di un object JSON consiste nel controllare innanzitutto se quell’object è una sequenza o meno:

 function recursivelyIterateProperties(jsonObject) { for (var prop in Object.keys(jsonObject)) { console.log(prop); if (!(typeof(jsonObject[prop]) === 'string') && !(jsonObject[prop] instanceof Array)) { recursivelyIterateProperties(jsonObject[prop]); } } } 

Se si desidera trovare le proprietà all’interno degli oggetti negli array , effettuare le seguenti operazioni:

 function recursivelyIterateProperties(jsonObject) { if (jsonObject instanceof Array) { for (var i = 0; i < jsonObject.length; ++i) { recursivelyIterateProperties(jsonObject[i]) } } else if (typeof(jsonObject) === 'object') { for (var prop in Object.keys(jsonObject)) { console.log(prop); if (!(typeof(jsonObject[prop]) === 'string')) { recursivelyIterateProperties(jsonObject[prop]); } } } }