2009-06-08 6 views
10

Gibt es einen generischen Ansatz zu verschachtelten Objekten auf einer Ebene „Komprimierung“:Komprimieren Objekthierarchien in JavaScript

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

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

Ich denke, es würde einige Rekursion beteiligt sein, aber ich dachte, ich muss nicht neu erfinden das Rad hier ...!?

+0

Warum würden Sie das brauchen? Willst du deine Javascripts während der Bauzeit verarbeiten und dann wird die Laufzeit verbessert? Aber wie würden Sie von Ihrer js (nach der Komprimierung) auf verschachtelte Objekte zugreifen, wenn Sie mit einem komplexen Objektmodell arbeiten müssen (wo zum Beispiel ein Unterobjekt (verschachteltes Objekt) als Argument an eine Funktion übergeben werden muss)? –

+1

Ich brauche dies für eine Datenzuordnung, wo die Verarbeitung verschachtelte Objekte nicht behandelt. – AnC

+0

Ich hatte ähnliche Anforderungen an AnC, wenn verschachtelte Objekte mit [Redis] (http://redis.io/commands#hash) verwendet werden, da es nur flache Hashes unterstützt. Ich endete mit einer [CoffeeScript-Version] (http://stackoverflow.com/questions/963607/compressing-object-hierarchies-in-javascript/6940124#6940124) von [Matthew Crumleys Lösung] (http://stackoverflow.com)/questions/963607/compressing-object-Hierarchies-in-javascript/965315 # 965315). –

Antwort

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; 
} 

Sie Mitglieder sind Mitglieder können geerbt von true in den zweiten Parameter.

ein paar Einschränkungen:

  • rekursive Objekte nicht funktionieren. Zum Beispiel:

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

    wird rekursiv, bis es eine Ausnahme auslöst.

  • Wie Ruquays Antwort zieht das Array-Elemente genau wie normale Objekteigenschaften heraus. Wenn Arrays intakt bleiben sollen, fügen Sie den Ausnahmen "|| prop instanceof Array" hinzu.

  • Wenn Sie Objekte aus einem anderen Fenster oder Rahmen aufrufen, werden Datumsangaben und reguläre Ausdrücke nicht berücksichtigt, da instanceof nicht ordnungsgemäß funktioniert. Sie können das Problem beheben, indem es mit dem Standard-toString Methode wie folgt ersetzt:

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

Wow, das scheint großartig zu funktionieren! Vielen Dank, auch für die ausführliche Dokumentation - ich weiß es zu schätzen! – AnC

4

Hier ist eine schnelle, aber aufgepasst, b/c wird es nicht arbeiten w/Arrays und Null-Werte (b/c ihre Typof gibt "Objekt" zurück).

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

Danke dafür. Es funktioniert noch nicht ganz (siehe Testobjekt unten); Ich werde darin nachforschen und jeden Fortschritt melden. (Wie bereits erwähnt, ich erwartet hatte dies ein Problem gelöst war - das heißt, dass es eine fertige Funktion in einigen JavaScript Kochbuch würde ...) var myObj = { \t a1: "Ebene 1", \t a2: { \t \t b1: 99, \t \t b2: { \t \t \t c1: new Date(), \t \t \t c2: "Level 3" \t \t}, \t \t b3: "asd" } \t, \t a3:/foo/ }; – AnC

2

Hier ist eine schnelle CoffeeScript Version basiert weg Matthew Crumley's answer (I nicht includePrototype verwendet haben, da ich keine Notwendigkeit, es hatte):

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 

und eine grundlegende Verflachung-Version, die mit wiederholten Separatoren und andere solche Verschlagenheit zweifellos scheitern würde:

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, ich verwende diese Funktionen, um Objektdiagramme in Redis hashes zu schieben, die nur eine einzelne Tiefe von Schlüssel/Wert-Paaren unterstützen.