javascript 多维数组,相加,并去重!

var arr =[{a:1,b:2,c:2},{a:1,b:2,c:3},{a:2,b:3,c:2},{a:2,b:3,c:2},{a:1,b:4,c:2}];

arr 是个多维数组,每个数组里面有多个属性abcde……,我举例只写了abc3个属性,
我想实现遍历数组后,当a和b的属性值相同的时候(只要求a和b),那么就把他们的c相加,
最终实现数组是

arr2 =[{a:1,b:2,c:5},{a:2,b:3,c:4},{a:1,b:4,c:2}];
阅读 3.6k
4 个回答
var arr =[{a:1,b:2,c:2},{a:1,b:2,c:3},{a:2,b:3,c:2},{a:2,b:3,c:2},{a:1,b:4,c:2}];

var SEPERATOR = '_'

function groupBy(arr, cb){
  var group = {}
  
  arr.forEach(function(e){
    var k = cb && cb(e)
    if(Object.prototype.toString.call(k) == '[object String]') {
      if(!group[k]) group[k] = [e]
      else group[k].push(e)
    }
  })
  
  return group
}

var data = groupBy(arr, function(e){
  return e.a + SEPERATOR + e.b
})

var results = []

for(k in data) {
  if(data.hasOwnProperty(k)) {
    var keys = k.split(SEPERATOR)
    var a = parseInt(keys[0])
    var b = parseInt(keys[1])
    var c = data[k].reduce(function(a, e){ return a + e.c}, 0)
    results.push({a: a, b: b, c: c})
  }
}

console.log(results)

如有错误,还望指正,大神轻喷


更新几点:

  • 这里的 groupBy 仅仅是为了使用分组的功能,如果你使用了诸如 underscore、lodash 或者 ramda 之类的工具库,可以找到对应方法
  • 另外我看题主编辑了问题,额外增加了要去重的条件,这个也很好实现,因为并说没有指明去重的标准,我就不改原来的答案了,只需在转化后的数据中加入一些特定的去重逻辑就可以了
  • 关于另外两个答案我大概看了一下,思路也挺好的,利用 reduce 边迭代边合并
var arr = [{a: 1, b: 2, c: 2}, {a: 1, b: 2, c: 3}, {a: 2, b: 3, c: 2}, {a: 2, b: 3, c: 2}, {a: 1, b: 4, c: 2}];

//要做对比的键名数组
const attrs = ['a', 'b'];

const result = arr.reduce((list, o) => {
  const item = list.find(n => !attrs.find(attr => o[attr] !== n[attr]));
  !item ? list.push({...o}) : item.c += o.c;
  return list;
}, []);

console.log(result)

这个问题本质上是一个SQL语句里的分组求和(SUM and GROUP BY)问题,既然如此,不妨动用牛刀杀鸡:

npm install --save alasql

然后:

var alasql = require('alasql');
var arr =[{a:1,b:2,c:2},{a:1,b:2,c:3},{a:2,b:3,c:2},{a:2,b:3,c:2},{a:1,b:4,c:2}];
var res = alasql('SELECT a, b, SUM(c) AS c FROM ? GROUP BY a, b', [arr]);
console.log(res);

结果:

[ { a: 1, b: 2, c: 5 },
  { a: 2, b: 3, c: 4 },
  { a: 1, b: 4, c: 2 } ]
const arr = [
    { a: 1, b: 2, c: 2 },
    { a: 1, b: 2, c: 3 },
    { a: 2, b: 3, c: 2 },
    { a: 2, b: 3, c: 2 },
    { a: 1, b: 4, c: 2 }
];

const result = arr.reduce((agg, item) => {
    const found = agg.find(t => t.a === item.a && t.b === item.b);
    if (found) {
        found.c += item.c;
    } else {
        agg.push({...item});
    }
    return agg;
}, []);

console.log(result);

写完了发现跟 @asseek 的思路差不多。

不过 @asseekattrs.find() 可以改成 attrs.every()

推荐问题