代码之家  ›  专栏  ›  技术社区  ›  Dallas

更好的方法将JSON转换为数组,以便在C3中绘制时间序列?

  •  0
  • Dallas  · 技术社区  · 7 年前

    我正在将一些JSON数据转换成一种格式,用于在C3中绘制时间序列,该格式将数组作为列输入。我有一个唯一的x值数组,但我想不出一个很好的方法来获得y值。一旦我完成了这个,我需要用numSold添加y2,但我想如果我能解决这个问题,我就能解决这部分。

    这是我需要的数据的最终格式:

    [
      ["x", "9-2-17", "9-4-17", "10-2-17"],
      ['item1-cost', 100, 150, 10],
      ['item3-cost', 200, null, 20],
      ...
    ]
    

    没有设置日期数或项目数。这是我从数据库里读到的。项目编号不一定是连续的。我们只关心绘制有数据项的数据。但不会有每个日期项目组合的数据。当这种情况发生时,我需要插入一个null来指示该项没有该日期的数据。项目编号为1索引。

    除了简单的JavaScript函数之外,我可以使用lo dash或D3中的任何函数来解决这个问题,但我试图避免难以阅读的代码和效率低下的代码。我觉得其他人肯定有这个需要,而且肯定有某种函数,比如我应该使用的过滤函数。

    我目前的实现没有我想要的那么高效,而且有点难以阅读。我使用项目编号作为索引,并生成一个稀疏数组,稍后将进行压缩。以下是psudocode:

    For every d object in data
      For the i index of d.date in uniqueDates
        If values[d.item] is undefined
          Fill values[d.item]] with null for uniqueDates.length
        Set values[d.item][i] to d.cost
    Convert values to dense format for graphing
    

    这是我玩的小提琴的链接:

    https://jsfiddle.net/dbkidd/q3r3moqu/

    var data = [
      {date: '9-2-17', item: 1, cost: 100},
      {date: '9-2-17', item: 3, cost: 200},
      {date: '9-4-17', item: 1, cost: 150},
      /* '9-4-17' does not have an entry for item 3 so cost should be counted as null */
      {date: '10-2-17', item: 1, cost: 10},
      {date: '10-2-17', item: 3, cost: 20}
    ]
    
    var uniqueDates = _.uniq(_.flatMap(data, 'date'));
    uniqueDates.unshift('x');
    
    var values = [];
    values.push(uniqueDates);
    
    function getLabel(index) {
      return 'item' + index + '-' + 'cost';
    }
    
    for (var d = 0; d < data.length; d++) {
      var i = _.indexOf(uniqueDates, data[d].date);
      if (data[d].item !== undefined) {
        var item = data[d].item;
        if (values[item] === undefined) {
            values[item] = _.fill(Array(uniqueDates.length), null);
            values[item][0] = getLabel(item);
        }
        values[item][i] = data[d].cost;
        }
    }
    
    function checkIfUndefined(x) {
        return (x !== undefined);
    }
    
    function sparseToDense(data) {
      return data.filter(checkIfUndefined);
    }
    
    values = sparseToDense(values);
    
    1 回复  |  直到 7 年前
        1
  •  1
  •   ktilcu    7 年前

    我对这个问题很感兴趣,并提出了第一个版本。它和你的有点不同,但在这里。我会注意到,我没有尝试对日期进行排序或将项目重命名为有用的内容,但可以添加这些内容。

    var data = [
    	{date: '9-2-17', item: 1, cost: 100},
      {date: '9-2-17', item: 3, cost: 200},
      {date: '9-4-17', item: 1, cost: 150},
      /* '9-4-17' does not have an entry for item 3 so cost should be counted as null */
      {date: '10-2-17', item: 1, cost: 10},
      {date: '10-2-17', item: 3, cost: 20},
      {date: '10-3-17', item: 2, cost: 2000}
    ]
    
    // utility functions
    const product = (...sets) =>
      sets.reduce((acc, set) =>
        _.flatten(acc.map(x => set.map(y => [ ...x, y ]))),
        [[]]);
    
    // the meat and potatoes
    const builder = lookup => pairs => pairs.reduce((agg, [item, date]) => {
       const out = _.cloneDeep(agg);
       const value = _.get(lookup, [date, item, 'cost'], null);
       const evalue = _.get(out, date, []);
       evalue.push(value);
       _.set(out, date, evalue);
       return out;
    }, {})
    
    // setup data structures for searching
    const byDateByItem = _.mapValues(_.groupBy(data, 'date'), x => _.keyBy(x, 'item'));    
    const items = _.uniq(data.map(x=>x.item));
    const dates = _.uniq(data.map(x=>x.date));
    
    // create all posibilities
    const pairs = product(items, dates);
    // populate possibilities with values
    const hash = builder(byDateByItem)(pairs);
    // put keys with values in their respective lists
    const p = _.toPairs(hash).map(_.flatten);
    // smash data into a matrix type thing
    const table = [['x',...items], ...p];
    // flip the table on it's side (╯°□°)╯︵ ┻━┻
    const out = _.zip(...table);
    console.log('out', out);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>