【问题标题】:Reduce array of objects on key and sum value into array减少键上的对象数组并将值求和到数组中
【发布时间】:2016-10-02 16:14:42
【问题描述】:

我有以下对象:

data = [
  { name: 'foo', type: 'fizz', val: 9 },
  { name: 'foo', type: 'buzz', val: 3 },
  { name: 'bar', type: 'fizz', val: 4 },
  { name: 'bar', type: 'buzz', val: 7 },
];

并使用了lodash地图:

result = _.map(data, function item, idx){
  return {
    key: item[key],
    values: item.value,
  }
}

结果:

[
  { key: foo, val: 9 },
  { key: foo, val: 3 },
  { key: bar, val: 4 },
  { key: bar, val: 7 },
]

但现在我正在尝试返回:

[
  { key: 'foo', val: 12 },
  { key: 'bar', val: 11 },
]

我尝试使用 reduce,它似乎只输出到单个对象,然后我可以将其转换回数组,但我觉得必须有一种优雅的方式来使用 lodash 从我的源数据直接转到我想要的没有所有中间步骤的结果。

我认为 this 正在解决我的确切问题,但将对象转换为上述所需的对象数组似乎需要做很多工作。

干杯。

【问题讨论】:

    标签: arrays json underscore.js lodash


    【解决方案1】:

    使用 groupBy 而不是 reduce 来进行初始分组的公认答案的一个转折:

    var result = _.chain(data)
        .groupBy('name')
        .map((group, key) => ({ key, val : _.sumBy(group, 'val') }))
        .value();
    

    【讨论】:

    • 如果允许使用 es6,那就完全不同了:) 例如 memo[obj.name = obj.val] += obj.val;,去掉未定义的检查,return {key, val}
    【解决方案2】:

    有趣的是不是直截了当,因为想通过键累加值,但又想把键作为属性键的值。所以有点像逆映射reduce:

    var result = 
        _.chain(data)
            .reduce(function(memo, obj) {
                if(typeof memo[obj.name] === 'undefined') {
                    memo[obj.name] = 0;
                } 
                memo[obj.name] += obj.val;
                return memo;
            }, {})
            .map(function (val, key) {
                return {key: key, val: val};
            })
            .value();
    

    在 es6 中为了简洁起见:

    _.chain(data)
        .reduce((memo, obj) => {
            memo[obj.name = obj.val] += obj.val;
            return memo; 
        }, {})
        .map((val, key) => ({key, val}))   
        .value();
    

    【讨论】:

    • 太棒了。谢谢你。不稳定的最终结果是输入到 d3 中,这似乎有时会出现古怪的数据结构。
    【解决方案3】:

    您可以使用map()uniq() 获取所有唯一的names,然后使用sumBy() 获取每个名称的map() 以获取它们各自的总和。

    var result = _(data)
      .map('name')
      .uniq()
      .map(key => ({ 
        key, 
        val: _(data).filter({ name: key }).sumBy('val')
      }))
      .value();
    

    var data = [
      { name: 'foo', type: 'fizz', val: 9 },
      { name: 'foo', type: 'buzz', val: 3 },
      { name: 'bar', type: 'fizz', val: 4 },
      { name: 'bar', type: 'buzz', val: 7 }
    ];
    
    var result = _(data)
      .map('name')
      .uniq()
      .map(key => ({ 
        key, 
        val: _(data).filter({ name: key }).sumBy('val')
      }))
      .value();
    
    console.log(result);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.13.1/lodash.js"></script>

    这里是非 es6 版本:

    var result = _(data)
      .map('name')
      .uniq()
      .map(function(key) { 
        return {
          key: key, 
          val: _(data).filter({ name: key }).sumBy('val')
        };
      })
      .value();
    

    【讨论】:

    • 哇,好优雅!
    • 实际上,我刚刚意识到您的总数不正确。它们包括所有项目。
    • 你说得对,我更新了答案。我想分组它会是最佳解决方案,然后使用 sumBy()。
    【解决方案4】:

    这是一个简单的非 lodash 版本。

    const data = [{
        name: 'foo',
        type: 'fizz',
        val: 9
      },
      {
        name: 'foo',
        type: 'buzz',
        val: 3
      },
      {
        name: 'bar',
        type: 'fizz',
        val: 4
      },
      {
        name: 'bar',
        type: 'buzz',
        val: 7
      },
    ]
    
    const result = data.reduce((acc, curr) => {
      const index = acc.findIndex(item => item.name === curr.name)
      index > -1 ? acc[index].val += curr.val : acc.push({
        name: curr.name,
        val: curr.val
      })
      return acc
    }, [])
    
    console.log(result)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-05-18
      • 2021-02-26
      • 1970-01-01
      • 2021-05-06
      • 2010-11-10
      • 2018-09-03
      • 2020-05-22
      • 1970-01-01
      相关资源
      最近更新 更多