Reputation: 299
There exists an array of objects like so where there is a 'category' key and some 'series' keys.
arrOne = [
{
"series_1": 25,
"category": "Category 1",
"series_2": 50
},
{
"series_1": 11,
"category": "Category 2",
"series_2": 22
},
{
"series_1": 32,
"category": "Category 1",
"series_2": 74
},
{
"series_1": 74,
"category": "Category 3",
"series_2": 98
},
{
"series_1": 46,
"category": "Category 3",
"series_2": 29
},
]
(Note that 'category' can be pretty much any value, though there will likely be multiple similar values as well as some unique values e.g. there are multiple objects with 'category' value 'Category 3' but only 1 with 'category' value 'Category 2')
The following lines of code will add up all of series_1 for objects with the same category
var objForAllCategories = {};
this.arrOne.forEach(item => {
if (objForAllCategories.hasOwnProperty(item.category))
objForAllCategories[item.category] = objForAllCategories[item.category] + item.series_1;
else
objForAllCategories[item.category] = item.series_1;
});
for (var prop in objForAllCategories) {
this.allCategoriesAndValues.push({
category: prop,
series_1: objForAllCategories[prop]
});
}
So it would result in:
allCategoriesAndValues = [
{
"category": "Category 1",
"series_1": 57 // 25 + 32 adding up series_1 from all 'Category 1' items in arrOne
},
{
"category": "Category 2",
"series_1": 11 // only 1 'Category 2' from arrOne
},
{
"category": "Category 3",
"series_1": 120 // 74 + 46 adding up series_1 from all 'Category 3' items in arrOne
}
]
However, I want to be able to add not just series_1 but also all other items.
This example only has category and series_1 and series_2 as keys. However, there could be:
How can I account for all potential series_x?
Intended result:
allCategoriesAndValues = [
{
"category": "Category 1",
"series_1": 57,
"series_2": 124,
..... if 'series_3', 'series_4' etc. existed, it would be included in this as above
},
{
"category": "Category 2",
"series_1": 11,
"series_2": 22,
..... if 'series_3', 'series_4' etc. existed, it would be included in this as above
},
{
"category": "Category 3",
"series_1": 120,
"series_2": 127,
..... if 'series_3', 'series_4' etc. existed, it would be included in this as above
}
]
Upvotes: 4
Views: 108
Reputation: 1619
Here is how I would do it
const res = arrOne.reduce((acc, { category, ...vals }) => {
if (acc[category]) {
Object.entries(vals).forEach(([ key, val ]) => acc[category][key] = acc[category][key] ? acc[category][key] + val : val);
} else {
acc[category] = vals;
}
return acc;
}, {});
Upvotes: 0
Reputation: 3490
You could just iterate over the array of objects and then the keys of each object, storing into a buffer object. You just have to either check for the existence of each key and add it if missing, or you can just coalesce falsey keys into a default value, like I did. I remove the category key from the object after I get its value, so that I don't have to try to skip it during the key iteration.
const arrOne = [
{"series_1": 25, "category": "Category 1", "series_2": 50},
{"series_1": 11, "category": "Category 2", "series_2": 22},
{"series_1": 32, "category": "Category 1", "series_2": 74},
{"series_1": 74, "category": "Category 3", "series_2": 98},
{"series_1": 46, "category": "Category 3", "series_2": 29},
];
let buffer = {};
arrOne.forEach(i=>{
let c = i.category;
buffer[c] = buffer[c] || {};
delete i.category;
Object.keys(i).forEach(k=>{
buffer[c][k] = buffer[c][k] || 0;
buffer[c][k] += i[k];
});
});
console.log(buffer);
let final = Object.keys(buffer).map(k=>{return {[k]: buffer[k]}});
console.log(final);
If you don't need have this in an array, the last step is optional. It only exists to transform the object into an array.
Upvotes: 0
Reputation: 22265
I will do that this way...
const arrOne =
[ { series_1: 25, category: 'Category 1', series_2: 50 }
, { series_1: 11, category: 'Category 2', series_2: 22 }
, { series_1: 32, category: 'Category 1', series_2: 74 }
, { series_1: 74, category: 'Category 3', series_2: 98 }
, { series_1: 46, category: 'Category 3', series_2: 29 }
]
console.time('chrono')
const allCategoriesAndValues =
Object.entries(
arrOne.reduce((r,{ category, ...series })=>
{
let cat = r[category] = r[category] ?? {}
Object.entries(series).forEach(([sName,val]) => cat[sName] = (cat[sName] ?? 0) + val);
return r
},{})
).map(([category,series])=>({category,...series}))
console.timeEnd('chrono')
console.log( allCategoriesAndValues )
.as-console-wrapper {max-height: 100%!important;top:0 }
Upvotes: 0
Reputation: 164730
Create a Map to collate series sums by category.
Then create an array from that map with the keys as category
const arr1 = [{"series_1":25,"category":"Category 1","series_2":50},{"series_1":11,"category":"Category 2","series_2":22},{"series_1":32,"category":"Category 1","series_2":74},{"series_1":74,"category":"Category 3","series_2":98},{"series_1":46,"category":"Category 3","series_2":29}]
const t1 = performance.now()
const cats = arr1.reduce((map, { category, ...series }) =>
map.set(category, Object.entries(series)
.reduce((s, [ key, count ]) => ({
...s,
[ key ]: (s[key] ?? 0) + count
}), map.get(category) ?? {})
), new Map())
const allCategoriesAndValues = Array.from(cats, ([ category, series ]) => ({
category,
...series
}))
const t2 = performance.now()
console.info(allCategoriesAndValues)
console.log(`Took ${t2 - t1}ms`)
.as-console-wrapper { max-height: 100% !important; }
Upvotes: 0
Reputation: 31987
To handle the multiple properties logic, you can loop through each property and check whether it matches the regex series_\d+
. If it does, you know that it is a property that you need to increment, and handle it accordingly (a property existence check is also necessary, as pointed out by Jayce444).
The following solution uses Array.reduce
. In the reducer function, it checks whether the accumulator array contains an item with the same category
property as the one currently being looped through. If it does, it will increment the appropriate properties. Otherwise, it will push the current item to the accumulator array.
arrOne=[{series_1:25,category:"Category 1",series_2:50},{series_1:11,category:"Category 2",series_2:22},{series_1:32,category:"Category 1",series_2:74},{series_1:74,category:"Category 3",series_2:98},{series_1:46,category:"Category 3",series_2:29,series_3:50}];
const res = arrOne.reduce((a, b) => {
let found = a.find(e => e.category == b.category)
if (found) {
Object.keys(b).forEach(e => {
if (/series_\d+/g.test(e)) found[e] = found[e] ? found[e] + b[e] : b[e];
})
} else {
a.push(b)
}
return a;
}, [])
console.log(res)
Upvotes: 2
Reputation: 5410
Something like this could work.
arrOne = [ { "series_1": 25, "category": "Category 1", "series_2": 50 }, { "series_1": 11, "category": "Category 2", "series_2": 22 }, { "series_1": 32, "category": "Category 1", "series_2": 74 }, { "series_1": 74, "category": "Category 3", "series_2": 98 }, { "series_1": 46, "category": "Category 3", "series_2": 29 },];
const result = [];
arrOne.reduce((acc, {category, ...series}) => {
if (acc.has(category)) {
Object.entries(series).forEach(([key, value]) => {
if (key.startsWith('series_')) {
acc.get(category)[key] = (acc.get(category)[key] || 0) + value;
}
});
} else {
const item = {category, ...series};
result.push(item);
acc.set(category, item);
}
return acc;
}, new Map());
console.log(result);
Upvotes: 1