在对象数组中对具有相同对象属性 B 的 JavaScript 对象属性 A 值求和

Sum JavaScript object propertyA values with the same object propertyB in an array of objects

本文关键字:对象 属性 JavaScript 求和 数组      更新时间:2023-09-26

如何获取JavaScript对象数组,例如

objArr = [
    {key:"Mon Sep 23 2013 00:00:00 GMT-0400", val:42},
    {key:"Mon Sep 24 2013 00:00:00 GMT-0400", val:78},
    {key:"Mon Sep 25 2013 00:00:00 GMT-0400", val:23},
    {key:"Mon Sep 23 2013 00:00:00 GMT-0400", val:54} // <- duplicate key
]

并通过对值求和来合并重复的键?

为了得到这样的东西:

reducedObjArr = [
    {key:"Mon Sep 23 2013 00:00:00 GMT-0400", val:96},
    {key:"Mon Sep 24 2013 00:00:00 GMT-0400", val:78},
    {key:"Mon Sep 25 2013 00:00:00 GMT-0400", val:23}
]

我尝试迭代并添加到新数组,但这不起作用:

var reducedObjArr = [];
var item = null, key = null;
for(var i=0; i<objArr.length; i++) {
    item = objArr[i];
    key = Object.keys(item)[0];
    item = item[key];
    if(!result[key]) {
        result[key] = item;
    } else {
       result[key] += item;
    }
}a

与其使用 for 循环和推送值,不如直接使用 map 和 reduce:

let objArr = [
  {key: 'Mon Sep 23 2013 00:00:00 GMT-0400', val: 42},
  {key: 'Mon Sep 24 2013 00:00:00 GMT-0400', val: 78},
  {key: 'Mon Sep 25 2013 00:00:00 GMT-0400', val: 23},
  {key: 'Mon Sep 23 2013 00:00:00 GMT-0400', val: 54}
];
// first, convert data into a Map with reduce
let counts = objArr.reduce((prev, curr) => {
  let count = prev.get(curr.key) || 0;
  prev.set(curr.key, curr.val + count);
  return prev;
}, new Map());
// then, map your counts object back to an array
let reducedObjArr = [...counts].map(([key, value]) => {
  return {key, value}
})
console.log(reducedObjArr);

您应该将

每个未找到的对象分配给结果及其 .key 属性。

如果找到,则需要添加其.val

var temp = {};
var obj = null;
for(var i=0; i < objArr.length; i++) {
   obj=objArr[i];
   if(!temp[obj.key]) {
       temp[obj.key] = obj;
   } else {
       temp[obj.key].val += obj.val;
   }
}
var result = [];
for (var prop in temp)
    result.push(temp[prop]);

另外,部分问题在于您重用item变量来引用.key的值,因此您丢失了对该对象的引用。

比其他地方发布更简单的reduce,因为它不使用Map元素

const objArr = [
{key:"Mon Sep 23 2013 00:00:00 GMT-0400", val:42},
{key:"Mon Sep 24 2013 00:00:00 GMT-0400", val:78},
{key:"Mon Sep 25 2013 00:00:00 GMT-0400", val:23},
{key:"Mon Sep 23 2013 00:00:00 GMT-0400", val:54}];
const output = objArr.reduce((accumulator, cur) => {
  let date = cur.key;
  let found = accumulator.find(elem => elem.key === date)
  if (found) found.val += cur.val;
  else accumulator.push(cur);
  return accumulator;
}, []);
console.log(output)

您可以使用哈希表按key进行分组。

var array = [{ key: 'Mon Sep 23 2013 00:00:00 GMT-0400', val: 42 }, { key: 'Mon Sep 24 2013 00:00:00 GMT-0400', val: 78 }, { key: 'Mon Sep 25 2013 00:00:00 GMT-0400', val: 23 }, { key: 'Mon Sep 23 2013 00:00:00 GMT-0400', val: 54}],
    grouped = [];
array.forEach(function (o) {
    if (!this[o.key]) {
        this[o.key] = { key: o.key, val: 0 };
        grouped.push(this[o.key]);
    }
    this[o.key].val += o.val;
}, Object.create(null));
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }

另一种方法是收集Map中的所有键/值对,并使用Array.from和对象的回调格式化最终数组。

var array = [{ key: 'Mon Sep 23 2013 00:00:00 GMT-0400', val: 42 }, { key: 'Mon Sep 24 2013 00:00:00 GMT-0400', val: 78 }, { key: 'Mon Sep 25 2013 00:00:00 GMT-0400', val: 23 }, { key: 'Mon Sep 23 2013 00:00:00 GMT-0400', val: 54 }],
    grouped = Array.from(
        array.reduce((m, { key, val }) => m.set(key, (m.get(key) || 0) + val), new Map),
        ([key, val]) => ({ key, val })
    );
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }

var objArr = [
{key:"Mon Sep 23 2013 00:00:00 GMT-0400", val:42},
{key:"Mon Sep 24 2013 00:00:00 GMT-0400", val:78},
{key:"Mon Sep 25 2013 00:00:00 GMT-0400", val:23},
{key:"Mon Sep 23 2013 00:00:00 GMT-0400", val:54}]
var targetObj = {};
for (var i = 0; i < objArr.length; i++) {
  if (!targetObj.hasOwnProperty(objArr[i].key)) {
    targetObj[objArr[i].key] = 0;
  }
  targetObj[objArr[i].key] += objArr[i].val;
}
console.log(targetObj);

可以将

Array#reduce与对象一起使用来存储每个键的值。

let arr = [{key:'Mon Sep 23 2013 00:00:00 GMT-0400', val:42},{key:'Mon Sep 24 2013 00:00:00 GMT-0400', val:78},{key:'Mon Sep 25 2013 00:00:00 GMT-0400', val:23},{key:'Mon Sep 23 2013 00:00:00 GMT-0400', val:54}];
let res = Object.values(arr.reduce((acc, curr)=>{
  (acc[curr.key] = acc[curr.key] || {key: curr.key, val: 0}).val += curr.val;
  return acc;
}, {}));
console.log(res);

在较新的浏览器中,可以使用逻辑空赋值。

let arr = [{key:'Mon Sep 23 2013 00:00:00 GMT-0400', val:42},{key:'Mon Sep 24 2013 00:00:00 GMT-0400', val:78},{key:'Mon Sep 25 2013 00:00:00 GMT-0400', val:23},{key:'Mon Sep 23 2013 00:00:00 GMT-0400', val:54}];
let res = Object.values(arr.reduce((acc, curr)=>{
  (acc[curr.key] ??= {key: curr.key, val: 0}).val += curr.val;
  return acc;
}, {}));
console.log(res);

试试这个。它应该有帮助。

var arr1 = [
  { name: 'besart', value: 12 },
  { name: 'astrit', value: 10 },
  { name: 'astrit', value: 10 },
  { name: 'besar', value: 18 },
  { name: 'besar', value: 3 },
  { name: 'astrit', value: 3 },
  { name: 'besart', value: 3 },
  { name: 'besart', value: 10 },
  { name: 'besar', value: 0 },
];
var arr2 = [];
var emri = "";
var value = 0;
for (var i = 0; i < arr1.length; i++) {
  emri = arr1[0].name;
  value += arr1[0].value;
  for (var j = 1; j < arr1.length; j++) {
    if (emri == arr1[j].name) {
      value += arr1[j].value;
      arr1.splice(j, 1);
      j--;
    }
  }
  arr1.splice(0, 1);
  arr2[i] = {
    name: emri,
    value: value
  };
  value = 0;
}
console.log(arr2);

下面是另一个仅使用一个循环(while 循环)的解决方案:

var arr1 = [
  { name: 'besart', value: 12 },
  { name: 'astrit', value: 10 },
  { name: 'astrit', value: 10 },
  { name: 'besar', value: 18 },
  { name: 'besar', value: 3 },
  { name: 'astrit', value: 3 },
  { name: 'besart', value: 3 },
  { name: 'besart', value: 10 },
  { name: 'besar', value: 0 },
];
var arr2 = [];
var emri = "";
var value = 0;
var i = 1;
var j = 0;
while (arr1.length != 0) {
  emri = arr1[0].name;
  if (emri == arr1[i].name) {
    value += arr1[i].value;
    arr1.splice(i, 1);
    i--;
  }
  i++;
  if (i == arr1.length) {
    value += arr1[0].value;
    i = 1;
    arr2[j] = {
      name: emri,
      value: value
    };
    j++;
    value = 0;
    arr1.splice(0, 1);
  }
}
console.log(arr2)

你也可以尝试使用JavaScript linq框架,它与SQL语句完全相同,SQL语句以较少的代码给出所需的输出,并且有效,可以在linq.js中找到。

var objArr =
[
{key:'Mon Sep 23 2013 00:00:00 GMT-0400', val:42},
{key:'Mon Sep 24 2013 00:00:00 GMT-0400', val:78},
{key:'Mon Sep 25 2013 00:00:00 GMT-0400', val:23},
{key:'Mon Sep 23 2013 00:00:00 GMT-0400', val:54}
];

var aggregatedObject = Enumerable.From(objArr)
        .GroupBy("$.key", null,
                 function (key, g) {
                     return {
                       key: key,
                       contributions: g.Sum("$.val")
                     }
        })
        .ToArray();
console.log(aggregatedObject);
<script src="http://cdnjs.cloudflare.com/ajax/libs/linq.js/2.2.0.2/linq.min.js"></script>

与循环相比,这很容易。

最近我需要一个类似的实现,我使用了某个使用reduce函数的人提供的类似解决方案。

几天后,我想自己实现类似的东西,这就是结果。

const users = [
        { id: 1, name: 'ernest', spent: 40 },
        { id: 2, name: 'ernest', spent: 40 },
        { id: 3, name: 'astrit', spent: 22 },
        { id: 4, name: 'astrit', spent: 2956 },
        { id: 5, name: 'astrit', spent: 22 },
        { id: 6, name: 'besart', spent: 40 },
        { id: 7, name: 'besart', spent: 100},
        { id: 8, name: 'besart', spent: 4000 }
    ];
const sum = [];
users.forEach(el => {
    if(sum.length === 0) {
        delete el.id;
        sum.push(el);
    }
    else
    {
        const get = () => {
            for(let i = 0; i < sum.length; i++) {
                if(sum[i].name === el.name) {
                    return { stat: true, id: i };
                }
            }
        }
        let i = get();
        if(i) {
            sum[i.id].spent += el.spent;
        }
        else
        {
            delete el.id;
            sum.push(el);
        }
    }
});
console.log(sum);

输出:

[ { name: 'ernest', spent: 80 }, { name: 'astrit', spent: 3000 }, { name: 'besart', spent: 4140 } ]

这是您的替代方案,但类似于爆炸药丸。它重用原始数组,而不是创建一个新的数组或不同的对象。这种排序可能不是必需的,并且会稍微减慢速度,但可以将其删除。

JavaScript

function reduceMyObjArr(arr) {
    var temp = {},
        index;
    for (index = arr.length - 1; index >= 0; index -= 1) {
        key = arr[index].key;
        if (temp.hasOwnProperty(key)) {
            arr[temp[key]].val += arr[index].val;
            arr.splice(index, 1);
        } else {
            temp[key] = index;
        }
    }
    arr.sort(function (a, b) {
        if (a.key === b.key) {
            return 0;
        }
        if (a.key < b.key) {
            return -1;
        }
        return 1;
    });
    return arr;
}
var myObjArr = [{
    key: "Mon Sep 23 2013 00: 00: 00 GMT - 0400",
    val: 42
}, {
    key: "Mon Sep 24 2013 00: 00: 00 GMT - 0400",
    val: 78
}, {
    key: "Mon Sep 25 2013 00: 00: 00 GMT - 0400",
    val: 23
}, {
    key: "Mon Sep 23 2013 00: 00: 00 GMT - 0400",
    val: 54
}];
reduceMyObjArr(myObjArr);
console.log(myObjArr);

js小提琴

还有一个 jsperf,它将这个(有和没有排序)与公认的答案进行比较。您可以通过扩展数据集来改进性能测试。

function sumGroupBy(array, groupColumn, valueColumn) {
    var res = []
    array.forEach((item) => {
         if(res.map((el) => el[groupColumn]).includes(item[groupColumn])) {
             res.filter((el) => el[groupColumn] == item[groupColumn])[0][valueColumn] += item[valueColumn]
         } else {
             eval(`res.push({${groupColumn}: "${item[groupColumn]}", ${valueColumn}: ${item[valueColumn]} })`)
         }
    });
    return res;
}
const pets = [
    {type:"Dog", age:12},
    {type:"Cat", age:13},
    {type:"Dog", age:6}, 
    {type:"Cat", age:18}
];
    
console.log(sumGroupBy(pets, 'type', 'age' ))
// [ { type: 'Dog', age: 18 }, { type: 'Cat', age: 31 } ]

下面是一个更通用的函数,可用于根据指定的键groupKey对对象数组进行分组,并计算指定属性sumKey的总和:

function groupAndSum(arr, groupKey, sumKey) {
  return Object.values(
    arr.reduce((acc, curr) => {
      const group = curr[groupKey];
      acc[group] = acc[group] || {
        [groupKey]: group,
        [sumKey]: 0
      };
      acc[group][sumKey] += curr[sumKey];
      return acc;
    }, {})
  );
}

演示:

let objArr = [{ key: 'Mon Sep 23 2013 00:00:00 GMT-0400', val: 42 }, { key: 'Mon Sep 24 2013 00:00:00 GMT-0400', val: 78 }, { key: 'Mon Sep 25 2013 00:00:00 GMT-0400', val: 23 }, { key: 'Mon Sep 23 2013 00:00:00 GMT-0400', val: 54 } ];
function groupAndSum(arr, groupKey, sumKey) {
  return Object.values(
    arr.reduce((acc, curr) => {
      const group = curr[groupKey];
      acc[group] = acc[group] || {
        [groupKey]: group,
        [sumKey]: 0
      };
      acc[group][sumKey] += curr[sumKey];
      return acc;
    }, {})
  );
}
console.log(groupAndSum(objArr, 'key', 'val'))

function mergeDuplicatesBy(array, getKey, mergeWith) {
  const buff = {}
  array.forEach(function (arrayElement) {
    const key = getKey(arrayElement)
    const alreadyExistingVal = buff[key]
    if (alreadyExistingVal) {
      buff[key] = mergeWith(alreadyExistingVal, arrayElement)
    } else {
      buff[key] = arrayElement
    }
  })
  return Object.values(buff)
}
mergeDuplicatesBy(
  arr,
  x => x.name,
  (x, y) => ({ name: x.name, foo: x.foo + y.foo })
)