Javascript多组数据,如sql group by和sum

时间:2016-07-15 07:11:32

标签: javascript underscore.js lodash

我一直在解决这个问题,为chart.js data source生成2个数组。

let data = [
    { date: '7/6/2016', tenant: 'BiggeCo', template: 'Meeting Room', count: 100 },
    { date: '7/6/2016', tenant: 'BiggeCo', template: 'All Hands MR', count: 200 },
    { date: '7/6/2016', tenant: 'SmallCo', template: 'Meeting Room', count: 10 },
    { date: '7/6/2016', tenant: 'SmallCo', template: 'All Hands MR', count: 110 },
    { date: '8/6/2016', tenant: 'BiggeCo', template: 'Meeting Room', count: 120 },
    { date: '8/6/2016', tenant: 'BiggeCo', template: 'All Hands MR', count: 30 },
    { date: '8/6/2016', tenant: 'SmallCo', template: 'Meeting Room', count: 60 },
    { date: '8/6/2016', tenant: 'SmallCo', template: 'All Hands MR', count: 70 }
];

给定此输入数据并通过可变数量的列分组,例如在我的示例名称和租户中,但可以只是1,比如日期,生成标签键,然后是图表数据集数据的总和。

目标标签输出:

let targetLabels = [
    ['7/6/2016', 'BiggeCo'],
    ['7/6/2016', 'SmallCo'],
    ['8/6/2016', 'BiggeCo'],
    ['8/6/2016', 'SmallCo']
];

目标数据集输出:

let targetDataSets = [
    {
        label: 'Count',
        data: [300, 120, 150, 130]
    }
];

到目前为止,我已经得到了以下内容,但它似乎很笨拙,而且我不确定如何获得计数列的总和并轻松地允许可变数量的列。

let temp = _.transform(data, function(result, value, key) {
    if (!_.some(result, function (r) { return r[0] == value['date'] && r[1] == value['tenant']; })) {
        result.push([ value['date'], value['tenant'] ]);
    }
}, []);

在SQL中我只写

SELECT name, tenant, sum(count) FROM table GROUP BY name, tenant

有人可以提供任何指导吗?

这看起来很接近我想要做的但我想在多个属性

上做

How can I reduce array with non-unique elements by summing their second value with lodash/underscore?

5 个答案:

答案 0 :(得分:3)

这是一个lodash解决方案:

let fields = ['date', 'tenant']; // fields for ordering and picking
let groupFn = v => [v.date, v.tenant]; // grouping date and tenant
let sumBy = v => _.sumBy(v, 'count'); // get sum by `count`
let countBy = group => _(group[0]) 
  .pick(fields) // pick `date` and `tenant` from the first item
  .assign({ count: _.sumBy(group, 'count') }) // assign the total `count`
  .value();

let source = _(data)
  .orderBy(fields) // order data by `date` and `tenant`
  .groupBy(groupFn) // group items by `date` and `tenant`
  .map(countBy); // get count of each group

// get `date` and `tenant` values only
let targetLabels = source.map(groupFn).value();

let targetDataSets = [
  {
    label: 'count',
    // get `count` values only
    data: source.map('count').value()
  }
];

// `date`, `tenant` and total `count` values only
let unified = source.value();

let data = [{
  date: '7/6/2016',
  tenant: 'BiggeCo',
  template: 'Meeting Room',
  count: 100
}, {
  date: '8/6/2016',
  tenant: 'SmallCo',
  template: 'All Hands MR',
  count: 70
}, {
  date: '7/6/2016',
  tenant: 'BiggeCo',
  template: 'All Hands MR',
  count: 200
}, {
  date: '7/6/2016',
  tenant: 'SmallCo',
  template: 'Meeting Room',
  count: 10
}, {
  date: '7/6/2016',
  tenant: 'SmallCo',
  template: 'All Hands MR',
  count: 110
}, {
  date: '8/6/2016',
  tenant: 'BiggeCo',
  template: 'Meeting Room',
  count: 120
}, {
  date: '8/6/2016',
  tenant: 'BiggeCo',
  template: 'All Hands MR',
  count: 30
}, {
  date: '8/6/2016',
  tenant: 'SmallCo',
  template: 'Meeting Room',
  count: 60
}];

let fields = ['date', 'tenant']; // fields for ordering and picking
let groupFn = v => [v.date, v.tenant]; // grouping date and tenant
let sumBy = v => _.sumBy(v, 'count'); // get sum by `count`
let countBy = group => _(group[0]) 
  .pick(fields) // pick `date` and `tenant` from the first item
  .assign({ count: _.sumBy(group, 'count') }) // assign the total `count`
  .value();

let source = _(data)
  .orderBy(fields) // order data by `date` and `tenant`
  .groupBy(groupFn) // group items by `date` and `tenant`
  .map(countBy); // get count of each group

// get `date` and `tenant` values only
let targetLabels = source.map(groupFn).value();

let targetDataSets = [
  {
    label: 'count',
    // get `count` values only
    data: source.map('count').value()
  }
];

// `date`, `tenant` and total `count` values only
let unified = source.value();

document.body.innerHTML = 
  '<strong>Data Sets</strong>' + 
  '<pre>' + JSON.stringify(targetDataSets, 0, 4) + '</pre><hr>' +
  '<strong>Labels</strong>' + 
  '<pre>' + JSON.stringify(targetLabels, 0, 4) + '</pre><hr>' +
  '<strong>Unified Value</strong>' + 
  '<pre>' + JSON.stringify(unified, 0, 4) + '</pre>';
<script src="https://cdn.jsdelivr.net/lodash/4.13.1/lodash.min.js"></script>

答案 1 :(得分:1)

假设data始终排序,您可以尝试这样的事情:

逻辑

  • 循环数据并检查下一个值。如果相同,则添加他们的count并继续,否则推入临时数组。
  • 如果最后2个元素相同,请检查长度并再次推入温度。
  • 现在使用此数组获取必要的值。

示例

&#13;
&#13;
var data=[{date:"7/6/2016",tenant:"BiggeCo",template:"Meeting Room",count:100},{date:"7/6/2016",tenant:"BiggeCo",template:"All Hands MR",count:200},{date:"7/6/2016",tenant:"SmallCo",template:"Meeting Room",count:10},{date:"7/6/2016",tenant:"SmallCo",template:"All Hands MR",count:110},{date:"8/6/2016",tenant:"BiggeCo",template:"Meeting Room",count:120},{date:"8/6/2016",tenant:"BiggeCo",template:"All Hands MR",count:30},{date:"8/6/2016",tenant:"SmallCo",template:"Meeting Room",count:60},{date:"8/6/2016",tenant:"SmallCo",template:"All Hands MR",count:70}];

var result = [];
data.reduce(function(c, n, i) {
  if (c) {
    if (c.date === n.date && c.tenant === n.tenant) {
      n.count += c.count;
      if (i === data.length - 1)
        result.push(n);
    } else {
      result.push(c);
    }
  }
  return n;
}, null)

var targetLabels = [];
var targetDataset = [{
  label: "count",
  data: []
}];
result.forEach(function(o) {
  targetLabels.push([o.date, o.tenant]);
  targetDataset[0].data.push(o.count);
})

console.log(targetLabels)
console.log(targetDataset)
&#13;
&#13;
&#13;

对于未分类的情况,您可以尝试手动排序,

data.sort(function(a, b) {
  if (a.date === b.date) {
    return a.tenant > b.tenant ? 1 : a.tenant < b.tenant ? -1 : 0;
  } else {
    return a.date > b.date ? 1 : a.date < b.date ? -1 : 0;
  }
})

Sample Fiddle

答案 2 :(得分:0)

只要我理解正确,你想按日期分组(我看不到属性名称)和tennant。

var data = [{ date: '7/6/2016', tenant: 'BiggeCo', template: 'Meeting Room', count: 100 }, { date: '7/6/2016', tenant: 'BiggeCo', template: 'All Hands MR', count: 200 }, { date: '7/6/2016', tenant: 'SmallCo', template: 'Meeting Room', count: 10 }, { date: '7/6/2016', tenant: 'SmallCo', template: 'All Hands MR', count: 110 }, { date: '8/6/2016', tenant: 'BiggeCo', template: 'Meeting Room', count: 120 }, { date: '8/6/2016', tenant: 'BiggeCo', template: 'All Hands MR', count: 30 }, { date: '8/6/2016', tenant: 'SmallCo', template: 'Meeting Room', count: 60 }, { date: '8/6/2016', tenant: 'SmallCo', template: 'All Hands MR', count: 70 }],
    result = []

data.forEach(function (a) {
    var key = a.date + '|' + a.tenant;
    if (!this[key]) {
        this[key] = { date: a.date, tenant: a.tenant, count: 0 };
        result.push(this[key]);
    }
    this[key].count += a.count;
}, Object.create(null));

console.log(result);

答案 3 :(得分:0)

我想,

.reduce应该这样做。您可能必须确保数据已排序。

&#13;
&#13;
let data = [
  { date: '7/6/2016', tenant: 'BiggeCo', template: 'Meeting Room', count: 100 },
  { date: '7/6/2016', tenant: 'BiggeCo', template: 'All Hands MR', count: 200 },
  { date: '7/6/2016', tenant: 'SmallCo', template: 'Meeting Room', count: 10 },
  { date: '7/6/2016', tenant: 'SmallCo', template: 'All Hands MR', count: 110 },
  { date: '8/6/2016', tenant: 'BiggeCo', template: 'Meeting Room', count: 120 },
  { date: '8/6/2016', tenant: 'BiggeCo', template: 'All Hands MR', count: 30 },
  { date: '8/6/2016', tenant: 'SmallCo', template: 'Meeting Room', count: 60 },
  { date: '8/6/2016', tenant: 'SmallCo', template: 'All Hands MR', count: 70 }
];

let targetDataSets  = [{
  label: 'Count',
  data: []
}];

let targetLabels  = [];

data.reduce( (p, c) => {
  let cnt = 0;
  if(p.hasOwnProperty('date')) {
    if(p.date === c.date && p.tenant === c.tenant) {
      cnt = p.count + c.count;
      targetLabels.push([c.date, c.tenant]);
      targetDataSets[0]['data'].push(cnt);
    }
  }
  return c;
},{})

console.log(targetLabels)
console.log(targetDataSets)
&#13;
&#13;
&#13;

答案 4 :(得分:0)

受到答案的启发,我坐下来正确地学习了lodash groupBymapreduce

我的目标以及为什么我将问题视为搜索我无法找到执行以下操作的解决方案:

  1. 根据问题,允许用户定义的属性数组分组
  2. 允许用户定义的“sum”属性,可以将其扩展到更多功能,例如将来等等。
  3. 在我的应用程序中已经有了lodash,我确实设法让LINQ.js大致相同,但我不喜欢在Lodash这样做的时候添加另一个依赖项。
  4. 解决方案:

    function group (data, groupKeys, sumKey) {
        return _.chain(data)
                .groupBy(function (d) {
                    let grouping = '';
    
                    for (let groupKey of groupKeys) {
                        grouping += d[groupKey] + '|||';
                    }
    
                    return grouping;
                })
                .map(function (groupedRow) {
                    let newRow = {};
    
                    // Extract the grouped properties to the new row
                    for (let groupKey of groupKeys) {
                        newRow[groupKey] = groupedRow[0][groupKey];
                    }
    
                    // Could use native reduce, browser support?
                    // Aggregate the sumKey property to the new row
                    newRow[sumKey] = _.reduce(groupedRow, function (sum, r) {
                        return sum + r[sumKey];
                    }, 0);
    
                    return newRow;
                })
                .value();
    }
    
    
    let groupKeys = ['date', 'tenant'];
    
    let sumKey = 'count';
    
    let temp4 = group(data, groupKeys, sumKey);
    
    console.log('output', temp4);
    
    console.log('labels', 
        _.map(temp4, function (row) {
            let newRow = {};
    
            for (let groupKey of groupKeys) {
                newRow[groupKey] = row[groupKey];
            }
    
            return newRow;
        })
    );
    
    console.log('dataSets', 
        _.map(temp4, sumKey)
    );
    

    输出:

    phil $ node index.js
    output
    [ { date: '7/6/2016', tenant: 'BiggeCo', count: 300 },
      { date: '7/6/2016', tenant: 'SmallCo', count: 120 },
      { date: '8/6/2016', tenant: 'BiggeCo', count: 150 },
      { date: '8/6/2016', tenant: 'SmallCo', count: 130 } ]
    
    labels
    [ { date: '7/6/2016', tenant: 'BiggeCo' },
      { date: '7/6/2016', tenant: 'SmallCo' },
      { date: '8/6/2016', tenant: 'BiggeCo' },
      { date: '8/6/2016', tenant: 'SmallCo' } ]
    
    dataSets [ 300, 120, 150, 130 ]