2009-06-08 17 views
10

Esiste un approccio generico alla "compressione" oggetti nidificati a un solo livello:compressione gerarchie di oggetti in JavaScript

var myObj = { 
    a: "hello", 
    b: { 
     c: "world" 
    } 
} 

compress(myObj) == { 
    a: "hello", 
    b_c: "world" 
} 

Credo che ci sarebbe qualche ricorsione coinvolti, ma ho pensato non ho bisogno di reinventare la ruota qui ...!?

+0

Perché ne hai bisogno? Vuoi elaborare i tuoi javascript durante il periodo di costruzione e le prestazioni di runtime js saranno migliorate? Ma come fareste ad accedere agli oggetti nidificati da js (dopo la compressione) se avete bisogno di operare con un modello di oggetti complesso (dove per esempio subobject (oggetto nidificato) deve passare come argomento ad una funzione)? –

+1

Ho bisogno di questo per una mappatura dei dati in cui l'elaborazione non gestisce oggetti nidificati. – AnC

+0

Ho avuto un simile bisogno di AnC quando uso oggetti nidificati con [Redis] (http://redis.io/commands#hash) in quanto supporta solo gli hash flat. Ho finito per usare una [versione di CoffeeScript] (http://stackoverflow.com/questions/963607/compressing-object-hierarchies-in-javascript/6940124#6940124) della [soluzione di Matthew Crumley] (http://stackoverflow.com/domande/963.607/compressione-oggetto-gerarchie-in-javascript/965315 # 965.315). –

risposta

21
function flatten(obj, includePrototype, into, prefix) { 
    into = into || {}; 
    prefix = prefix || ""; 

    for (var k in obj) { 
     if (includePrototype || obj.hasOwnProperty(k)) { 
      var prop = obj[k]; 
      if (prop && typeof prop === "object" && 
       !(prop instanceof Date || prop instanceof RegExp)) { 
       flatten(prop, includePrototype, into, prefix + k + "_"); 
      } 
      else { 
       into[prefix + k] = prop; 
      } 
     } 
    } 

    return into; 
} 

È possibile includere membri ereditati membri passando true nel secondo parametro.

Qualche distinguo:

  • oggetti ricorsivi non funzionerà. Per esempio:

    var o = { a: "foo" }; 
    o.b = o; 
    flatten(o); 
    

    sarà ricorsivamente fino a quando viene generata un'eccezione.

  • Come la risposta di ruquay, questo estrae elementi di array proprio come le normali proprietà dell'oggetto. Se si desidera mantenere intatti gli array, aggiungere "|| prop instanceof Array" alle eccezioni.

  • Se si chiama questo su oggetti da una finestra o frame diversi, le date e le espressioni regolari non saranno incluse, poiché instanceof non funzionerà correttamente. È possibile risolvere che sostituendolo con il metodo di default toString in questo modo:

    Object.prototype.toString.call(prop) === "[object Date]" 
    Object.prototype.toString.call(prop) === "[object RegExp]" 
    Object.prototype.toString.call(prop) === "[object Array]" 
    
+0

Wow, sembra funzionare alla grande! Grazie mille, anche per la documentazione dettagliata - lo apprezzo davvero! – AnC

4

Ecco uno veloce, ma attenzione, b/c sarà non lavoro w/matrici e valori nulli (b/c il loro tipo di ritorni "oggetto").

var flatten = function(obj, prefix) { 
    if(typeof prefix === "undefined") { 
    prefix = ""; 
    } 
    var copy = {}; 
    for (var p in obj) { 
    if(obj.hasOwnProperty(p)) { 
     if(typeof obj[p] === "object") { 
     var tmp = flatten(obj[p], p + "_"); 
     for(var q in tmp) { 
      if(tmp.hasOwnProperty(q)) { 
      copy[prefix + q] = tmp[q]; 
      } 
     } 
     } 
     else { 
     copy[prefix + p] = obj[p]; 
     } 
    } 
    } 
    return copy; 
} 

var myObj = { 
    a: "level 1", 
    b: { 
    a: "level 2", 
    b: { 
     a: "level 3", 
     b: "level 3" 
    } 
    } 
} 

var flattened = flatten(myObj); 
+0

Grazie per questo. Non funziona ancora abbastanza (vedi oggetto di prova sotto); scaverà dentro e riferirà qualsiasi progresso qui. (Come accennato prima, mi aspettavo che questo era un problema risolto - vale a dire che ci sarebbe stata una funzione di ready-made in qualche libro di cucina JavaScript ...) var myObj = { \t a1: "livello 1", \t a2: { \t \t b1: 99, \t \t b2: { \t \t \t c1: new Date(), \t \t \t c2: "livello 3" \t \t}, \t \t b3: "asd" \t}, \t a3:/foo/ }; – AnC

2

Ecco una rapida versione CoffeeScript in base al largo Matthew Crumley's answer (non ho usato includePrototype come ho avuto alcun bisogno di esso):

flatten = (obj, into = {}, prefix = '', sep = '_') -> 
    for own key, prop of obj 
    if typeof prop is 'object' and prop not instanceof Date and prop not instanceof RegExp 
     flatten prop, into, prefix + key + sep, sep 
    else 
     into[prefix + key] = prop 
    into 

e una versione unflatten base, che sarebbe senza dubbio fallire con separatori ripetuti e altri accorgimenti quali:

unflatten = (obj, into = {}, sep = '_') -> 
    for own key, prop of obj 
    subKeys = key.split sep 
    sub = into 
    sub = (sub[subKey] or= {}) for subKey in subKeys[...-1] 
    sub[subKeys.pop()] = prop 
    into 

FWIW, utilizzo queste funzioni per inserire i grafici degli oggetti in Redis hashes, che supportano solo una singola profondità di coppie chiave/valore.

Problemi correlati