2016-06-01 2 views
1

ÜbersichtLEFT JOIN von verschachtelten Arrays, Objekte und Eigenschaften in einer einzigen flachen Tisch

I Microsoft OData Query Builder library verwenden verschachtelte Tabellen zu erzeugen, aber ich brauche flachen Tisch. Um dies zu erreichen Ich habe 2 mögliche Lösungen,

  1. Ändern Sie die verschachtelte JSON I erhalten
  2. Let OData Query Builder eine flache Tabelle erstellen verschachtelte Tabellen erzeugen und dann ich wandeln sie in flachen Tisch.

Ich versuchte ersten Ansatz und mit ihm stecken - https://jsfiddle.net/e14oL103/10/ I-Ausgang so sein erwarten,

ID Name Products.0.ID 
999 Meat 17987 
999 Meat 17988 
999 Meat 17989 
999 Meat 17990 

Problem 2.en Ansatz

Zum Beispiel (What I haben), Wenn ich eine Tabelle wie diese habe,

http://image.prntscr.com/image/1fedddfc3cf142c5963a23a94c746612.png

Was will ich --- nur für das erste Stb aus dem Bild, wird Rest derselben folgen Logik

MainTable.Name Product.ID Product.Name Product.Description ProductDetail.Details 
Food    0   Bread  Whole gain bread  
Food    1   Milk   Low Fat Milk  Details of product 1 

Mein Szenario ist weitaus dynamischer und komplexer dann oben, aber ich bin nicht sicher, wo ich anfangen soll.

Warum ich denke, es

nicht duplizieren ist brauche ich nicht Array glätten, war ich das richtige Wort zu finden, nicht in der Lage, vor dem „Kartesisches Produkt“ ist. Ich kann Underscore.js verwenden, um Arrays zu reduzieren, aber das ist nicht das, was ich will, ich möchte Elemente von übergeordneten Arrays mit untergeordneten Arrays kombinieren.

+0

Suchen Sie nach einer Möglichkeit, „Lösung 2“ zu tun, und wenn dies der Fall wäre es möglich, die rohe html zu bekommen, was Sie jetzt haben Tabelle weise? Ich kann es nicht wirklich an dem Bild erkennen. – JonSG

Antwort

2

Das Snippet wurde geändert. Entschuldigung für den unordentlichen Code. Ich hoffe es hilft. (js fidle here)

/** 
    * @param {array} array - The data to be converted into flat view 
    * @param {array} schema - Accumulates names and represents names of the columns 
    * @param {array} record - Array with values accumulated so far with indexes corresponding to schema 
    * @param {array} path - Accumulates current path (names of all properties to go through to get to the object) 
    * @return {array} - Array of flat records with content of all nested objects 
    */ 
    function processMany(array, schema, record = [], path = []) { 
    var records = []; 
    if (array.length == 0) { 
     // there is no objects in current array, just return the record 
     records.push(record); 
    } else { 
     array.forEach(function(obj) { 
     var recordClone = record.slice(0); 
     var r = processSingle(obj, schema, recordClone, path); 
     // there are objects in current array, accumulate whatever is parsed from each object and return it 
     records = records.concat(r); 
     }); 
    } 
    return records; 
    } 

    /** 
    * @param {hash} object - The data to be converted into flat view 
    * @param {array} schema - Accumulates names and represents names of the columns 
    * @param {array} record - Array with values accumulated so far with indexes corresponding to schema 
    * @param {array} path - Accumulates current path (names of all properties to go through to get to the object) 
    * @return {array} - Array of flat records with content of all nested objects 
    */ 
    function processSingle(object, schema, record, path) { 
    var nestedObjects = [], 
     nestedArrays = []; 
    var records = []; 
    Object.keys(object).forEach(function(key) { 
     var value = object[key]; 
     // we need to treat differently arrays, objects and plain values 
     if (Array.isArray(value)) { 
     // it is array, save it for later 
     nestedArrays.push({ 
      key: key, 
      value: value 
     }); 
     } else if (value && value.constructor == Object) { 
     // it is object (hash), save it for later 
     nestedObjects.push({ 
      key: key, 
      value: value 
     }); 
     } else { 
     // it is plain value, just add it to record 
     var keyPath = path.concat(key); 
     record = processSimpleValue(value, schema, record, keyPath); 
     } 
    }); 
    if (nestedObjects.length == 0) { 
     // if there are no nested objects we have only one record so far 
     records = [record]; 
    } else { 
     // if there are nested objects, each of them has one or more records in it 
     // if a nested object contains more than one record each next nested object 
     // will fill each of the records returned by the previous nested object 
     records = [record]; 
     nestedObjects.forEach(function(keyValue) { 
     var thisObjectRecords = []; 
     records.forEach(function(record) { 
      var keyPath = path.concat(keyValue.key); 
      var recordClone = record.slice(0); 
      var object = keyValue.value; 
      var r = processSingle(object, schema, recordClone, keyPath) 
      thisObjectRecords = thisObjectRecords.concat(r); 
     }); 
     records = thisObjectRecords; // one or more records here 
     }); 
    } 
    if (nestedArrays.length == 0) { 
     // we have no nested arrays; it means we return everything we have processed so far 
     return records; 
    } else { 
     // each of the nested arrays will return one or more records, we need to accumulate it and return 
     var nestedRecords = []; 
     nestedArrays.forEach(function(keyValue) { 
     // we need to fill each of the records we have with information from nested arrays 
     records.forEach(function(record) { 
      var keyPath = path.concat(keyValue.key); 
      var recordClone = record.slice(0); 
      var array = keyValue.value; 
      var r = processMany(array, schema, recordClone, keyPath) 
      nestedRecords = nestedRecords.concat(r); // one or more records here 
     }); 
     }); 
     return nestedRecords; 
    } 
    } 

    /** 
    * @param {string|number|null} value - The data to be added to the record 
    * @param {array} schema - Accumulates names and represents names of the columns 
    * @param {array} record - Array with values accumulated so far with indexes corresponding to schema 
    * @param {array} path - Accumulates current path (names of all properties to go through to get to the object) 
    */ 
    function processSimpleValue(value, schema, record, path) { 
    var index = addToSchema(schema, path); 
    record[index] = value; 
    return record; 
    } 

    function pathToS(path) { 
    return path.join('.'); 
    } 

    /** 
    * Returns index of of the path in schema, adds the path to the schema if it is not present in it 
    * @return {number} index of current path in schema 
    */ 
    function addToSchema(schema, path) { 
    var pathS = pathToS(path); 
    var i = schema.indexOf(pathS); 
    if (i < 0) { 
     i = schema.length; 
     schema.push(pathS); 
    } 
    return i; 
    } 

    var flatHeaders = []; 
    var flatData = processMany(data, flatHeaders); 
+0

vielen Dank +1, ich sah auf Ihre Geige, es funktioniert sehr schön, aber es gibt ein Problem, wenn Produkt-Array leer ist, sollte es stattdessen leere Zellen hinzufügen, bitte sehen Sie hier ignoriert Milch Kategorie, wenn es keine gibt Produkte, ich möchte 1 Zeile nur dann hinzufügen und Spalten leer ruhen, https://jsfiddle.net/e14oL103/17/ Hier ist die Logik, dies zu tun - https://jsfiddle.net/e14oL103/18/, danke – Mathematics

+0

Es ist sehr einfach zu beheben. https: // jsfiddle.net/e14oL103/19/ –

+0

danke, ich bin kein Export in JS, deshalb finde ich es schwierig zu ändern, aber ich lerne, funktioniert Ihr Code mit jeder Ebene von verschachtelten Daten? Da ich einen dynamischen + komplexen Datensatz habe, wird hier keine dritte Ebene hinzugefügt - https://jsfiddle.net/e14oL103/20/ – Mathematics

0

Es ist in der Tat nicht flach. Sie können erreichen, was Sie wollen, mit der unten angegebenen Funktion. Es funktioniert auch, wenn mehrere Untertabellen vorhanden sind. Wenn Sie beispielsweise eine Untertabelle mit 3 Elementen und eine mit 4 Elementen haben, haben Sie insgesamt 3*4=12 Zeilen.

var data = [{ 
 
    "Products": [{ 
 
    "ID": 17987 
 
    }, { 
 
    "ID": 17988 
 
    }, { 
 
    "ID": 17989 
 
    }, { 
 
    "ID": 17990 
 
    }], 
 
    "ID": "999", 
 
    "Name": "Meat" 
 
}]; 
 

 
var oneToMany = function(data) { 
 

 

 
    var props = Object.keys(data).filter(function(x) { 
 
    return data.hasOwnProperty(x); 
 
    }); 
 

 
    var nonArrayProps = props.filter(function(x) { 
 
    return !Array.isArray(data[x]); 
 
    }); 
 

 
    var arrayProps = props.filter(function(x) { 
 
    return Array.isArray(data[x]); 
 
    }); 
 

 

 
    var baseObject = {}; 
 
    for (var i = 0; i < nonArrayProps.length; i++) { 
 
    baseObject[nonArrayProps[i]] = data[nonArrayProps[i]]; 
 
    } 
 

 
    var objects = [baseObject]; 
 
    for (var i = 0; i < arrayProps.length; i++) { 
 
    var prop = arrayProps[i]; 
 
    var array = data[prop]; 
 
    var newObjects = []; 
 
    for (var j = 0; j < array.length || j == 0; j++) { 
 
     for (var k = 0; k < objects.length || k == 0; k++) { 
 
     var newObj = clone(objects[k]); 
 
     newObj[prop] = array[j]; 
 
     newObjects.push(newObj); 
 
     } 
 
    } 
 
    objects = newObjects; 
 
    } 
 
    return objects; 
 
} 
 

 
function clone(obj) { 
 
    var clone = {}; 
 
    for (property in obj) { 
 
    if (obj.hasOwnProperty(property)) clone[property] = obj[property]; 
 
    } 
 
    return clone; 
 
} 
 

 
function normalizeTable(tableData) { 
 
    return Array.prototype.concat.apply([], tableData.map(oneToMany)); 
 
} 
 

 
console.log(normalizeTable(data));

+0

vielen Dank +1, ich sah auf Ihre Geige, es funktioniert sehr schön, aber es gibt ein Problem, wenn Produkt-Array leer ist, sollte es stattdessen leere Zellen hinzufügen, bitte sehen Sie hier ignoriert Milch Kategorie, wenn es keine gibt Produkte, ich möchte nur eine Zeile hinzufügen, dann Spalten leer, jsfiddle.net/e14oL103/17 Hier ist die Logik, dies zu tun - jsfiddle.net/e14oL103/18, danke – Mathematics

+0

@Mathematics Wenn Sie es so machen, es wird ein linker Join. Wenn Sie sicher sind, dass Sie das wollen, können Sie das auch tun. Bearbeitete die Lösung für die linke Verknüpfung anstelle der inneren Verknüpfung. –