2016-06-20 7 views
2

Ich habe folgendes JsonJavascript wie zurückzukehren Array von Karte

var myjson = [{ 

      "files": [ 
       { 
        "domain": "d", 
        "units": [ 
         { 
          "key": "key1", 
          "type": "2" 

        }, 
         { 
          "key": "key2", 
          "type": "2" 
        }, 
         { 
          "key": "key3", 
          "type": "2" 
        }] 

      }, 

       { 
        "domain": "d1", 
        "units": [ 
         { 
          "key": "key11", 
          "type": "2" 

        }, 
         { 
          "key": "key12", 
          "type": "2" 
        }, 
         { 
          "key": "key13", 
          "type": "2" 
        }] 

      }] 

      }, 

     { 

      "files": [ 
       { 
        "domain": "d", 
        "units": [ 
         { 
    ...... 

Ich möchte ein neues Array von diesem Json-Array erstellen. Die Länge des Arrays ist die Anzahl der "Einheiten" in diesem Json-Objekt.

Also muss ich "Einheiten" extrahieren und einige Daten von übergeordneten Objekten hinzufügen.

units: [{ 
     domain: "", 
     type: "", 
     key: "" 
    }, { 
     domain: "", 
     type: "", 
     key: "" 
    }, 
    { 
     domain: "", 
     type: "", 
     key: "" 
    } 
.... 
    ]; 

Ich denke, ich wahrscheinlich so etwas wie dies tun kann:

var res = []; 

myjson.forEach(function(row) { 
    row.files.forEach(function(tfile) { 
     tfile.units.forEach(function(unit) { 

      var testEntity = { 
       domain: tfile.domain, 
       type : unit.type, 
       key: unit.key 

      }; 

      res.push(testEntity); 

     }); 
    }); 
}); 

Aber es ist schwer zu lesen und sieht nicht so gut. Ich dachte, etwas zu tun wie:

var RESULT = myjson.map(function(row) { 
    return row.files.map(function(tfile) { 
     return tfile.units.map(function(unit) { 

      return { 
       domain: tfile.domain, 
       type : unit.type, 
       key: unit.key 

      }; 



     }); 
    }); 
}); 

Aber das funktioniert nicht und sieht nicht besser aus. Gibt es einen Weg dies zu tun, funktioniert es vielleicht deklarativer. Ich hoffe, Ramda.js könnte helfen.

Gibt es einen guten Ansatz im Allgemeinen, um Daten von jedem verschachtelten JSON in lesbarer Weise zu erhalten?

Implementierung so etwas wie:

nestedjson.findAllOnLastlevel(function(item){ 

return { 
key : item.key, 
type: type.key, 
domain : item.parent.domain} 

}); 

Oder irgendwie diese json abflachen, so dass alle Eigenschaften von allen Eltern Objekt bewegt Kinder blättert. myjson.flatten ("files.units")

jsbin http://jsbin.com/hiqatutino/edit?css,js,console

Vielen Dank

+0

so benötigen Sie eine funktionierende Version, die Sie bereits haben oder eine gut aussehende Version, die mit ES6 Stil erreicht werden könnte? –

+0

jetzt habe ich eine funktionierende hässliche Version, aber ich mache eine Menge von diesen "verschachtelten JSON-Operationen" War über einen richtigen Ansatz nachdenken. Vielleicht sogar eine allgemeine Herangehensweise. Der JSON, den ich zur Verfügung gestellt habe, ist vereinfacht. Es könnte tiefer sein. Auch ist "=>" in meinem Fall nicht verfügbar. – Ant

+0

Das Hauptproblem besteht darin, zu wissen, welche Eigenschaft iterieren soll. Sie könnten einen Pfad mit einem rekursiven Ansatz angeben, aber zumindest müssen Sie die Eigenschaften neu kombinieren. –

Antwort

5

Die Funktion, die Sie verwenden können, ist hier Ramda der R.chain Funktion statt R.map. Sie können sich R.chain als eine Möglichkeit vorstellen, eine Liste mit einer Funktion abzubilden, die eine andere Liste zurückgibt und die resultierende Liste von Listen dann zusammenfasst.

// get a list of all files 
const listOfFiles = 
    R.chain(R.prop('files'), myjson) 

// a function that we can use to add the domain to each unit 
const unitsWithDomain = 
    (domain, units) => R.map(R.assoc('domain', domain), units) 

// take the list of files and add the domain to each of its units 
const result = 
    R.chain(file => unitsWithDomain(file.domain, file.units), listOfFiles) 

Wenn Sie es noch einen Schritt weiter, dann könnten Sie auch R.pipeK verwenden, die mit dem Komponieren Funktionen zusammen, die wie R.chain zwischen jedem der gegebenen Funktionen verhalten hilft nehmen wollte.

// this creates a function that accepts the `myjson` list 
// then passes the list of files to the second function 
// returning the list of units for each file with the domain attached 
const process = pipeK(prop('files'), 
         f => map(assoc('domain', f.domain), f.units)) 

// giving the `myjson` object produces the same result as above 
process(myjson) 
+0

sieht gut aus - ich habe meinen JSON vereinfacht, wenn ich mehrere (fünf) Eigenschaften habe an der gleichen Stelle wie Domain zugeordnet sein? Und einige andere Eigenschaften auf der ersten Ebene prop1: "value," files ": {.... Ich habe hier http://jsbin.com/samilit/edit?css,js,console – Ant

+0

' var result = R.pipe hinzugefügt ( R.chain (Funktion (p) {return R.map (R. assoc ('Eltern', p), p.files)}), R.chain (Funktion (p) {return R.map (R .assoc ('parent', p), p.units)}), R.chain (Funktion (k)) {return { Domäne: k.parent.domain, prop1: k.parent.parent.prop1, Schlüssel: k.key}}) ) (myjson) ' – Ant

+0

Ja, das würde erfordern, die Eigenschaftenwerte als Status durch jeden Schritt einzufädeln, zB http://jsbin.com/vepumikuve/1/edit?js,console –

1

So etwas wie dies funktionieren sollte. .reduce ist ein guter für diese Art von Situationen.

const allUnits = myjson.reduce((acc, anonObj) => { 
    const units = anonObj.files.map(fileObj => { 
    return fileObj.units.map(unit => { 
     return {...unit, domain: fileObj.domain}) 
    }) 
    return [...acc, ...units] 
}, []) 

anzumerken, dass dies auf beiden Arrays Verbreitung beruht und Objekt ausbreitet, die ES6 sind Eigenschaften nicht von jeder Plattform unterstützt.

Wenn Sie ES6 nicht verwenden können, ist hier eine ES5-Implementierung. Nicht so schön, aber macht das Gleiche:

var allUnits = myjson.reduce(function (acc, anonObj) { 
    const units = anonObj.files.map(function(fileObj) { 
    // for each fileObject, return an array of processed unit objects 
    // with domain property added from fileObj 
    return fileObj.units.map(function(unit) { 
     return { 
     key: unit.key, 
     type: unit.type, 
     domain: fileObj.domain 
     } 
    }) 
    }) 
    // for each file array, add unit objects from that array to accumulator array 
    return acc.concat(units) 
}, []) 
+0

Ich habe ES5, und keine Möglichkeit, es zu ändern :( – Ant

0

Versuchen Sie, diese

var myjson = [{ 
 

 
"files": [{ 
 
    "domain": "d", 
 
    "units": [{ 
 
     "key": "key1", 
 
     "type": "2" 
 
    }, { 
 
     "key": "key2", 
 
     "type": "2" 
 
    }, { 
 
     "key": "key3", 
 
     "type": "2" 
 
    }] 
 
    }, 
 
    { 
 
    "domain": "d1", 
 
    "units": [{ 
 
     "key": "key11", 
 
     "type": "2" 
 

 
    }, { 
 
     "key": "key12", 
 
     "type": "2" 
 
    }, { 
 
     "key": "key13", 
 
     "type": "2" 
 
    }] 
 
    } 
 
] 
 
}]; 
 

 
//first filter out properties exluding units 
 
var result = []; 
 
myjson.forEach(function(obj){ 
 
    obj.files.forEach(function(obj2){ 
 
    result = result.concat(obj2.units.map(function(unit){ 
 
     unit.domain = obj2.domain; 
 
     return unit; 
 
    })); 
 
    }); 
 
}); 
 

 
console.log(result);

+0

funktioniert, kann etwas, das nicht benötigt var result = []; könnte getan werden? In unveränderliche Weise? Map immer ein Ergebnis zurück? Könnte es "Art von mehreren" zurückgeben – Ant

+0

'domain' key ist verpasst – RomanPerekhrest

+0

@Ant map auf allen drei Ebenen wird Ihnen etwas wie' [[[key: 'key1', Wert: '1'}]]]] zurückgeben und um dies zu überwinden, müssen Sie Code schreiben um einzelne Items zu akzeptieren und später zu einem Array zu verketten.Weil man einen kürzeren Code wollte, kann ich es nicht kürzer kriegen :) – gurvinder372

2

Pure JS ist sehr ausreichend, um das Ergebnis in einfachen Einzeilern zu erzeugen. Ich würde keine Bibliothek nur für diesen Job berühren. Ich habe zwei Möglichkeiten, es hier zu tun. Die erste ist eine Kette von reduce.reduce.map und die zweite ist eine Kette von reduce.map.map. Hier ist der Code;

var myjson = [{"files":[{"domain":"d","units":[{"key":"key1","type":"2"},{"key":"key2","type":"2"},{"key":"key3","type":"2"}]},{"domain":"d1","units":[{"key":"key11","type":"2"},{"key":"key12","type":"2"},{"key":"key13","type":"2"}]}]},{"files":[{"domain":"e","units":[{"key":"key1","type":"2"},{"key":"key2","type":"2"},{"key":"key3","type":"2"}]},{"domain":"e1","units":[{"key":"key11","type":"2"},{"key":"key12","type":"2"},{"key":"key13","type":"2"}]}]}], 
 
    units = myjson.reduce((p,c) => c.files.reduce((f,s) => f.concat(s.units.map(e => (e.domain = s.domain,e))) ,p) ,[]); 
 
    units2 = myjson.reduce((p,c) => p.concat(...c.files.map(f => f.units.map(e => (e.domain = f.domain,e)))) ,[]); 
 
    console.log(units); 
 
    console.log(units2);

Für ES5 Kompatibilität würde ich die reduce.reduce.map Kette vorschlagen, da es keine Notwendigkeit für einen Spread-Operator ist. Und ersetzen Sie die Pfeilfunktionen durch ihre konventionellen Gegenstücke wie das folgende;

var myjson = [{"files":[{"domain":"d","units":[{"key":"key1","type":"2"},{"key":"key2","type":"2"},{"key":"key3","type":"2"}]},{"domain":"d1","units":[{"key":"key11","type":"2"},{"key":"key12","type":"2"},{"key":"key13","type":"2"}]}]},{"files":[{"domain":"e","units":[{"key":"key1","type":"2"},{"key":"key2","type":"2"},{"key":"key3","type":"2"}]},{"domain":"e1","units":[{"key":"key11","type":"2"},{"key":"key12","type":"2"},{"key":"key13","type":"2"}]}]}], 
 
    units = myjson.reduce(function(p,c) { 
 
     \t      return c.files.reduce(function(f,s) { 
 
     \t      \t      return f.concat(s.units.map(function(e){ 
 
     \t      \t      \t       e.domain = s.domain; 
 
     \t      \t      \t       return e; 
 
     \t      \t      \t       })); 
 
     \t           },p); 
 
     \t     },[]); 
 
console.log(units);