First iterate through the array and push the 'name' into another object's property. If the property exists add the 'value' to the value of the property otherwise initialize the property to the 'value'. Once you build this object, iterate through the properties and push them to another array.
Here is some code:
Copyvar obj = [
{ 'name': 'P1', 'value': 150 },
{ 'name': 'P1', 'value': 150 },
{ 'name': 'P2', 'value': 200 },
{ 'name': 'P3', 'value': 450 }
];
var holder = {};
obj.forEach(function(d) {
if (holder.hasOwnProperty(d.name)) {
holder[d.name] = holder[d.name] + d.value;
} else {
holder[d.name] = d.value;
}
});
var obj2 = [];
for (var prop in holder) {
obj2.push({ name: prop, value: holder[prop] });
}
console.log(obj2);
Run code snippetEdit code snippet Hide Results Copy to answer Expand
Hope this helps.
Answer from link on Stack OverflowFirst iterate through the array and push the 'name' into another object's property. If the property exists add the 'value' to the value of the property otherwise initialize the property to the 'value'. Once you build this object, iterate through the properties and push them to another array.
Here is some code:
Copyvar obj = [
{ 'name': 'P1', 'value': 150 },
{ 'name': 'P1', 'value': 150 },
{ 'name': 'P2', 'value': 200 },
{ 'name': 'P3', 'value': 450 }
];
var holder = {};
obj.forEach(function(d) {
if (holder.hasOwnProperty(d.name)) {
holder[d.name] = holder[d.name] + d.value;
} else {
holder[d.name] = d.value;
}
});
var obj2 = [];
for (var prop in holder) {
obj2.push({ name: prop, value: holder[prop] });
}
console.log(obj2);
Run code snippetEdit code snippet Hide Results Copy to answer Expand
Hope this helps.
An ES2024 approach to group by name
You can first use Map.groupBy to group your array by name into Map where each key in the map is a name from your objects and the value is an array of objects from your array with that name key. You can then use Array.from() on the grouped Map to convert it into an array of objects, where for each object, you accumulate the objects to sum on value with the same name key using .reduce():
Copyconst sumByKey = (arr, key, value) => {
const grouped = Map.groupBy(arr, o => o[key]);
return Array.from(
grouped.values(),
group => group.reduce((acc, obj) => ({...obj, [value]: (acc[value] ?? 0) + obj[value]}), {})
);
}
const arr = [ { 'name': 'P1', 'value': 150 }, { 'name': 'P1', 'value': 150 }, { 'name': 'P2', 'value': 200 }, { 'name': 'P3', 'value': 450 } ];
console.log(sumByKey(arr, 'name', 'value')); // [ { "name": "P1", "value": 300 }, { "name": "P2", "value": 200 }, { "name": "P3", "value": 450 } ]
Run code snippetEdit code snippet Hide Results Copy to answer Expand
An ES6 approach to group by name:
You can convert your array of objects to a Map by using .reduce(). The Map has key-value pairs, where each key is the name, and each value is the accumulated sum of values for that particular name key. You can then easily convert the Map back into an array using Array.from(), where you can provide a mapping function that will take the keys/values of the Map and convert them into objects:
Copyconst arr = [ { 'name': 'P1', 'value': 150 }, { 'name': 'P1', 'value': 150 }, { 'name': 'P2', 'value': 200 }, { 'name': 'P3', 'value': 450 } ];
const res = Array.from(arr.reduce(
(m, {name, value}) => m.set(name, (m.get(name) || 0) + value), new Map
), ([name, value]) => ({name, value}));
console.log(res);
Run code snippetEdit code snippet Hide Results Copy to answer Expand
The above is quite compact and not necessarily the easiest to read. I would suggest putting it into a function so it's clearer what it's doing. If you're after more self-documenting code, using for...of can make the above easier to understand. The below function is also useful if you want to group or sum on keys with spaces in them:
Copyconst arr = [ { 'name': 'P1', 'value': 150 }, { 'name': 'P1', 'value': 150 }, { 'name': 'P2', 'value': 200 }, { 'name': 'P3', 'value': 450 } ];
const sumByKey = (arr, key, value) => {
const map = new Map();
for(const obj of arr) {
const currSum = map.get(obj[key]) || 0;
map.set(obj[key], currSum + obj[value]);
}
const res = Array.from(map, ([k, v]) => ({[key]: k, [value]: v}));
return res;
}
console.log(sumByKey(arr, 'name', 'value')); // 'name' = value to group by, 'value' = value to sum
Run code snippetEdit code snippet Hide Results Copy to answer Expand
Grouping by more than just name:
Here's an approach that should work if you have other overlapping properties other than just name (the keys/values need to be the same in both objects for them to "group"). It involves iterating through your array and reducing it to Map which holds key-value pairs. Each key of the new Map is a string of all the property values you want to group by, and so, if your object key already exists then you know it is a duplicate, which means you can add the object's current value to the stored object. Finally, you can use Array.from() to transform your Map of key-value pairs, to an array of just values:
Copyconst arr = [{'name':'P1','value':150},{'name':'P1','value':150},{'name':'P2','value':200},{'name':'P3','value':450}];
const res = Array.from(arr.reduce((acc, {value, ...r}) => {
const key = JSON.stringify(r);
const current = acc.get(key) || {...r, value: 0};
return acc.set(key, {...current, value: current.value + value});
}, new Map).values());
console.log(res);
Run code snippetEdit code snippet Hide Results Copy to answer Expand
javascript - Sum up nested array of objects based on keys - Code Review Stack Exchange
javascript - Sum array of objects by unique keys - Code Review Stack Exchange
reactjs - How to calculate sum of object keys in array - javascript - Stack Overflow
Sum up array of Javascript objects by key - Stack Overflow
Videos
You are returning a new object in each iteration inside the reducers, when you could use just one object for the tallies (see below). The handling of undefined values suggest to me that the underlying data might be invalid, in which case it makes more sense to fix the data and establish validation before insert. Unless of course the values are spec'd to be optional.
const sumKeys = (xs, ys = []) => {
const sum = {key1: 0, key2: 0}
for (const obj of xs) {
for (const item of obj.items) {
sum.key1 += item.key1 * obj.num
sum.key2 += item.key2 * obj.num
}
}
for (const obj of ys) {
sum.key1 += obj.gross * obj.num
sum.key2 += obj.net * obj.num
}
return sum
}
I found it useful to ensure that the input data was sanitised to remove the possibility of undefined values before further processing. Much of the verbosity of the code was to do with checking for undefined when the alternate value was always 0, and that even extended to the computeVal function which became redundant.
The sample data was actually 'complete' and didn't include undefined values so I've not tested for outcomes based on imcomplete data. It might also be reasonable to replace || 0 with nullish (?? 0) and to consider if there might be strings or other types that would coerce to falsy and be converted to 0 when something else should happen.
Whilst it would be possible to refactor the nested reduce, it doesn't seem worth it as it now is easier to understand.
I also chose to abbreviate the variable names, again principally to reduce the verbosity without introducing ambiguity.
I suspect that the actual application data doesn't use opaque keys like key1 and key2, I suspect this was used to simplify the question. As a result, this might need a little more work to provide useful defaults, and generally the naming conventions might need aligning with the actual data.
function sanitiseObjects(objects) {
return objects?.map(obj => {
return {
...obj,
num: obj.num || 0,
items: obj.items.map(item => ({
key1: item.key1 || 0,
key2: item.key2 || 0,
}))
}
}) || []
}
function sanitizeTotals(totals) {
return totals?.map(total => ({
...total,
num: total.num || 0,
gross: total.gross || 0,
net: total.net || 0,
})) || []
}
function sumUpKeys(objects, totals = undefined) {
const defaults = { key1: 0, key2: 0 }
const totalsSum = sanitizeTotals(totals).reduce((acc, item) => {
return {
key1: acc.key1 + (item.num * item.gross),
key2: acc.key2 + (item.num * item.net),
};
}, defaults);
const objectsSum = sanitiseObjects(objects).reduce((objectsAcc, object) => {
const itemsSum = object.items.reduce((itemsAcc, item) => {
return {
key1: (object.num * item.key1) + itemsAcc.key1,
key2: (object.num * item.key2) + itemsAcc.key2,
};
}, defaults);
return {
key1: objectsAcc.key1 + itemsSum.key1,
key2: objectsAcc.key2 + itemsSum.key2,
};
}, defaults);
// add additional services sum to the monthly charges sum
return {
key1: objectsSum.key1 + (totalsSum?.key1 || 0),
key2: objectsSum.key2 + (totalsSum?.key2 || 0),
};
}
const objects1 = [
{ num: 3, items: [{ key1: 4, key2: 2, }, { key1: 10, key2: 20, }, ], key3: 'aa', },
{ num: 4, items: [{ key1: 4, key2: 2, }, ], key3: 'aa', },
];
const totals1 = [
{ num: 2, gross: 10, net: 5,},
{ num: 3, gross: 6, net: 12,},
];
// result with totals
const result1 = sumUpKeys(objects1, totals1);
// result without totals
const result2 = sumUpKeys(objects1);
console.log(result1);
console.log(result2);
You can use Javascript's reduce function. This is basically the same as @epascarello answer but in ES6 syntax.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
const arr = [{level:2},{level:4},{level:5}];
const total = arr.reduce((prev,next) => prev + next.level,0);
console.log(total);
Either a simple loop
var a = [{level:1},{level:2},{level:3},{level:4}],
total = 0;
for (var i=0; i<a.length; i++) {
total += a[i].level;
}
console.log('total', total)
or reduce
var a = [{level:1},{level:2},{level:3},{level:4}]
console.log(a.reduce( function(cnt,o){ return cnt + o.level; }, 0))
As @HereticMonkey notes, this question is probably better suited to the Code Review community, as StackOverflow is generally for getting help solving specific problems rather than for discussing the quality of functional code. Just keep that in mind for next time.
That being said, yes, it is possible to use Array#reduce here, but that's not the simplest way. Two loops and some destructuring are all you need.
var array1 = [{'2019-07-13': 1, '2019-07-14': 4, '2019-07-15': 7}];
var array2 = [{'2019-07-13': 5, '2019-07-14': 8, '2019-07-15': 6}];
var array3 = [{'2019-07-13': 2, '2019-07-14': 1, '2019-07-15': 9}];
let mergedInput = [...array1, ...array2, ...array3];
let temp = {};
for (a of mergedInput) {
for (const [k, v] of Object.entries(a)) {
temp[k] = (temp[k]||0)+a[k];
}
}
console.log(temp);
If you're still interested in seeing an Array#reduce implementation, look in the revision history for my first answer.
You can accomplish this using Array.prototype.reduce -
const concat = (a = {}, b = {}) =>
Object
.entries(b)
.reduce
( (r, [ k, v ]) =>
({ ...r, [k]: (r[k] || 0) + v })
, a
)
const main = (...all) =>
all.reduce(concat, {})
var array1 =
[{'2019-07-13': 1, '2019-07-14': 4, '2019-07-15': 7}]
var array2 =
[{'2019-07-13': 5, '2019-07-14': 8, '2019-07-15': 6}]
var array3 =
[{'2019-07-13': 2, '2019-07-14': 1, '2019-07-15': 9}]
console.log(main(...array1, ...array2, ...array3))
// { '2019-07-13': 8
// , '2019-07-14': 13
// , '2019-07-15': 22
// }
Two problems: your total and obj arguments are backwards, and you need to provide something to initialize the total variable (that's the ,0 part)
arr.reduce((total, obj) => obj.credit + total,0)
// 3
You can use .forEach() here:
var arr = [{ 'credit': 1, 'trash': null }, { 'credit': 2, 'trash': null}];
var total = 0;
arr.forEach(item => {
total += item.credit;
});
console.log(total);
solved using reduce and forEach
Inside the reduce function I'm running a forEach on the array of keys of the incomes object/attribute. For each key which is a date I'm checking if the accumulator of the reduce function contains an attribute for each date and creates if not. After creating the attribute I'm summing the value for the current date attribute.
const data = [{
group: 'A',
incomes: {
"2019-12": 100,
"2020-12": 200,
"2021-12": 15
}
},
{
group: 'B',
incomes: {
"2019-12": 25,
"2020-12": 50,
}
}
]
const totalIncomes = data.reduce((acc, curr) => {
Object.keys(curr.incomes).forEach((key, index) => {
if (!acc[key]) {
acc[key] = 0
}
acc[key] += curr.incomes[key]
})
return acc
}, {})
console.log(totalIncomes)
Maybe this is not the pretties solutions but you can do it like this, the function is of course not necessary.
const data = [
{
group: "A",
incomes: {
"2019-12": 100,
"2020-12": 200,
"2021-12": 15,
},
},
{
group: "B",
incomes: {
"2019-12": 25,
"2020-12": 50,
},
},
];
getterInformation(data);
function getterInformation(object) {
let objectWithCalculatedValues = {};
object.forEach((items) => {
for (const key in items.incomes) {
if (objectWithCalculatedValues[key] === undefined) {
objectWithCalculatedValues[key] = 0;
}
objectWithCalculatedValues[key] += items.incomes[key];
}
});
console.log(objectWithCalculatedValues);
}
You need to iterate values, not keys:
let sums = {}
for (let obj of Object.values(returnObj))
sums[obj.page] = (sums[obj.page] ?? 0) + obj.total_sum
If you want to use more of a functional aproach
const sums = {}
Object.values(returnObj).forEach(item => {
sums[item.page] = (sums[item.page] ?? 0) + item.total_sum
})
Using the lodash lib:
const sums = {}
_.values(returnObj).forEach(item => {
sums[item.page] = (sums[item.page] ?? 0) + item.total_sum
})