将嵌套数组,对象和属性左连接到单个平面表中

时间:2016-06-01 14:05:25

标签: javascript jquery html

概述

我使用Microsoft OData Query Builder library生成嵌套表,但我需要平面表。为实现这一目标,我有两种可能的解决方案,

  1. 修改我收到的嵌套JSON以创建一个平面表
  2. 让OData Query Builder生成嵌套表,然后将它们转换为平面表。
  3. 我尝试了第一种方法并坚持下去 - https://jsfiddle.net/e14oL103/10/ 我期待输出就像这样,

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

    第二种方法的问题

    例如(我有什么) ,如果我有这样的表,

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

    我想要的东西 - 仅适用于图片中的第一个tr,其余部分将遵循相同的逻辑

    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
    

    我的情景比上面更加动态和复杂,但我不知道从哪里开始。

    为什么我认为它不重复

    我不需要扁平阵列,我无法找到正确的单词,之前是#34;笛卡尔积"。我可以使用underscore.js来压缩数组,但这不是我想要的,我想将父数组的元素与子数组结合起来。

2 个答案:

答案 0 :(得分:2)

更改了您的代码段。对不起凌乱的代码。希望能帮助到你。 (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);

答案 1 :(得分:0)

确实没有变平。您可以通过下面给出的功能实现您想要的效果。如果有多个子表,它也可以工作。例如,如果您有一个包含3个元素的子表和一个包含4个元素的子表,则总共会有3*4=12行。

&#13;
&#13;
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));
&#13;
&#13;
&#13;