Как объединить массив объектов с одинаковым значением свойства?
Есть массив объектов с кодом, видом услуги и суммой:
let Arr = [
{
serviceid: 1,
servicename : "Откопка ямы",
sum : 30
},
{
serviceid: 1,
servicename : "Откопка ямы",
sum : 50
},
{
serviceid: 2,
servicename : "Кладка плитки",
sum : 20
},
{
serviceid: 2,
servicename : "Кладка плитки",
sum : 25
},
{
serviceid: 3,
servicename : "Услуги трактора",
sum : 44
}
]
Видов услуг может быть много. Необходимо посчитать сумму с одинаковыми serviceid и на выходе получить объединенный массив объектов:
[
{
serviceid: 1,
servicename : "Откопка ямы",
sum : 80
},
{
serviceid: 2,
servicename : "Кладка плитки",
sum : 45
},
{
serviceid: 3,
servicename : "Услуги трактора",
sum : 44
}
]
Ответы (4 шт):
Могу предложить 2 варианта для суммирования элементов массива и возврата нового массива с агрегированными элементами:
Вариант для больших массивов
let Arr = [
{
serviceid: 1,
servicename : "Откопка ямы",
sum : 30
},
{
serviceid: 1,
servicename : "Откопка ямы",
sum : 50
},
{
serviceid: 2,
servicename : "Кладка плитки",
sum : 20
},
{
serviceid: 2,
servicename : "Кладка плитки",
sum : 25
},
{
serviceid: 3,
servicename : "Услуги трактора",
sum : 44
}
];
function calcArr(arr) {
const res = [];
const resSum = {}
arr.forEach(item => {
if (resSum[item.serviceid]) {
resSum[item.serviceid].sum += item.sum;
} else {
resSum[item.serviceid] = {
sum: item.sum,
servicename: item.servicename
}
}
});
for (let x in resSum) {
res.push({
serviceid: +x,
servicename: resSum[x].servicename,
sum: resSum[x].sum
})
}
return res;
}
console.log(calcArr(Arr));
Вариант для маленьких массивов
let Arr = [
{
serviceid: 1,
servicename : "Откопка ямы",
sum : 30
},
{
serviceid: 1,
servicename : "Откопка ямы",
sum : 50
},
{
serviceid: 2,
servicename : "Кладка плитки",
sum : 20
},
{
serviceid: 2,
servicename : "Кладка плитки",
sum : 25
},
{
serviceid: 3,
servicename : "Услуги трактора",
sum : 44
}
];
function reduceCalc(arr) {
return arr.reduce((acc, item) => {
const pos = acc.findIndex(it => it.serviceid === item.serviceid);
if (pos === -1) acc.push(item)
else acc[pos].sum += item.sum;
return acc;
}, [])
}
console.log(reduceCalc(Arr));
Времы выполнения:
| Элементов | Повторений | Время forEach+for | Время reduce |
|---|---|---|---|
| 5 | 10000 | 11.187ms | 3.74ms |
| 5 | 100000 | 63.639ms | 8.433ms |
| 1000 | 10000 | 88.678ms | 95.673ms |
| 1000 | 100000 | 862.047ms | 1.001s |
| 10000 | 10000 | 761.753ms | 1.631s |
| 10000 | 100000 | 7.529s | 16.435s |
| 100000 | 10000 | 7.563s | 17.191s |
| 100000 | 100000 | 1:14.519 (m:ss.mmm) | 3:02.386 (m:ss.mmm) |
Можно сделать так:
- Создаём словарь - new Map()
- Проходимся по элемнтам массива (reduce) и смотрим, если в словаре есть объект с данным
serviceid, то достаём его и добавляем к сумме значениеsum-curr.sum += obj.sum - Если нет, то добавлем новый элемент с суммой равной нулю -
groups.set(obj.serviceid, {...obj, sum: 0}) - Далее достаём из словаря все значения (.values()) и преобразуем их в массив с помощью синатксиса [...]
const arr = [{
serviceid: 1,
servicename: "Откопка ямы",
sum: 30
},
{
serviceid: 1,
servicename: "Откопка ямы",
sum: 50
},
{
serviceid: 2,
servicename: "Кладка плитки",
sum: 20
},
{
serviceid: 2,
servicename: "Кладка плитки",
sum: 25
},
{
serviceid: 3,
servicename: "Услуги трактора",
sum: 44
}
];
const groupArr = [...arr.reduce((groups, obj) => {
if (!groups.has(obj.serviceid)) groups.set(obj.serviceid, {...obj, sum: 0});
const curr = groups.get(obj.serviceid);
curr.sum += obj.sum;
return groups;
}, new Map()).values()];
console.log(groupArr);
Кину и свой камень в огород, видимо господа программисты не знают что find возвращает по референсу, и можно запросто менять объект прям в внутри массива.
let result = Arr.reduce((out, el, index, inp) => {
/** где
out - в первом шаге это 2й параметр функции reduce,в последующих то что возвращает return
el - текущий элемент итерации
index - порядковое число текущего элемента el
inp - сам массив целиком
*/
let last = out.find(e => e.serviceid === el.serviceid)
if (last) {
last.sum += el.sum
} else {
out.push(el)
}
return out
}, [])
Задающему вопрос хочу сказать, что-бы вас не минусовали и вопрос был уместен. Во первых пожалуйста изучите хотя-бы стандарты написания кода JS
- Google JavaScript Style Guide
- JSCS
Ваш код выглядит ужасно!
Во вторых задавайте вопрос по существу - не понимаю как работает reducer, поясните пожалуйста на моём примере
Предложу еще такой вариант... Всего один проход по массиву. Далее применяется объектный метод.
let Arr = [
{
serviceid: 1,
servicename : "Откопка ямы",
sum : 30
},
{
serviceid: 1,
servicename : "Откопка ямы",
sum : 50
},
{
serviceid: 2,
servicename : "Кладка плитки",
sum : 20
},
{
serviceid: 2,
servicename : "Кладка плитки",
sum : 25
},
{
serviceid: 3,
servicename : "Услуги трактора",
sum : 44
}
]
console.log(test(Arr))
//
function test(arr) {
const obj = arr.reduce((obj, o) => {
if (obj[o.serviceid]) obj[o.serviceid].sum += o.sum
else obj[o.serviceid] = {...o}
return obj
}, {})
return Object.values(obj)
}