This is probably remedial but I can't figure it out. I've tried using d3 and played with lodash to get an efficient solution, but didn't get anything close.
I have an array of objects in JavaScript. If the [Selected] value is true, I want to create an object grouped by [Version Name] with a count of distinct zones, and a sum of the totals per version. For instance with object...
[
{ Selcted: false, Version Name: "aaa", Zone: "11111", Value: 5 },
{ Selcted: false, Version Name: "aaa", Zone: "11111", Value: 10 },
{ Selcted: true, Version Name: "aaa", Zone: "11111", Value: 15 },
{ Selcted: true, Version Name: "aaa", Zone: "11111", Value: 20 },
{ Selcted: true, Version Name: "aaa", Zone: "22222", Value: 25 },
{ Selcted: true, Version Name: "bbb", Zone: "22222", Value: 30 },
{ Selcted: true, Version Name: "bbb", Zone: "22222", Value: 35 },
{ Selcted: true, Version Name: "bbb", Zone: "2222", Value: 40 }
]
Should return a result of
[
{ Version Name: "aaa", Zone Count: "2", Value Sum: 50 },
{ Version Name: "bbb", Zone Count: "1", Value Sum: 105 },
]
This uses my favorite groupBy function :) Once you get the groups, you do another group to get the zone count, and a reduce to get your sum.
In a nutshell
const byName = groupBy(input.filter(it => it.Selcted), it => it['Version Name'])
const output = Object.keys(byName).map(name => {
const byZone = groupBy(byName[name], it => it.Zone)
const sum = byName[name].reduce((acc, it) => acc + it.Value, 0)
return {
'Version Name': name,
ZoneCount: Object.keys(byZone).length,
ValueSum: sum
}
})
Don't forget, you need quotes around 'Version Name' to use it as a key.
Here's a working example with your dataset.
function groupBy(a, keyFunction) {
const groups = {};
a.forEach(function(el) {
const key = keyFunction(el);
if (key in groups === false) {
groups[key] = [];
}
groups[key].push(el);
});
return groups;
}
const input = [{
Selcted: false,
'Version Name': "aaa",
Zone: "11111",
Value: 5
},
{
Selcted: false,
'Version Name': "aaa",
Zone: "11111",
Value: 10
},
{
Selcted: true,
'Version Name': "aaa",
Zone: "11111",
Value: 15
},
{
Selcted: true,
'Version Name': "aaa",
Zone: "11111",
Value: 20
},
{
Selcted: true,
'Version Name': "aaa",
Zone: "22222",
Value: 25
},
{
Selcted: true,
'Version Name': "bbb",
Zone: "22222",
Value: 30
},
{
Selcted: true,
'Version Name': "bbb",
Zone: "22222",
Value: 35
},
{
Selcted: true,
'Version Name': "bbb",
Zone: "2222",
Value: 40
}
]
const byName = groupBy(input.filter(it => it.Selcted), it => it['Version Name'])
const output = Object.keys(byName).map(name => {
const byZone = groupBy(byName[name], it => it.Zone)
const sum = byName[name].reduce((acc, it) => acc + it.Value, 0)
return {
'Version Name': name,
ZoneCount: Object.keys(byZone).length,
ValueSum: sum
}
})
console.log(output)
You could use lodash and get the wanted counts after grouping by using
_
“Seq” Methods, for chaining lodash methods,_.groupBy
for grouping by "Version Name"
_.map
for the result sets with_.uniqBy
for counting distinct values,_.sumBy
for summing Value
and_.value
for getting an array with objects as result set.var data = [{ Selected: false, "Version Name": "aaa", Zone: "11111", Value: 5 }, { Selected: false, "Version Name": "aaa", Zone: "11111", Value: 10 }, { Selected: true, "Version Name": "aaa", Zone: "11111", Value: 15 }, { Selected: true, "Version Name": "aaa", Zone: "11111", Value: 20 }, { Selected: true, "Version Name": "aaa", Zone: "22222", Value: 25 }, { Selected: true, "Version Name": "bbb", Zone: "22222", Value: 30 }, { Selected: true, "Version Name": "bbb", Zone: "22222", Value: 35 }, { Selected: true, "Version Name": "bbb", Zone: "22222", Value: 40 }],
result = _(data)
.groupBy('Version Name')
.map((array, key) => ({
"Version Name": key,
"Zone Count": _.uniqBy(array, 'Zone').length,
"Value Sum": _.sumBy(array, 'Value')
}))
.value();
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>
Having used filter
to get the items you want, you can do this in two steps, with a reduce
followed by a map
let input = [
{ Selcted: false, "Version Name": "aaa", Zone: "11111", Value: 5 },
{ Selcted: false, "Version Name": "aaa", Zone: "11111", Value: 10 },
{ Selcted: true, "Version Name": "aaa", Zone: "11111", Value: 15 },
{ Selcted: true, "Version Name": "aaa", Zone: "11111", Value: 20 },
{ Selcted: true, "Version Name": "aaa", Zone: "22222", Value: 25 },
{ Selcted: true, "Version Name": "bbb", Zone: "22222", Value: 30 },
{ Selcted: true, "Version Name": "bbb", Zone: "22222", Value: 35 },
{ Selcted: true, "Version Name": "bbb", Zone: "2222", Value: 40 }
]
var result = input.filter(x => x.Selcted)
.reduce( (acc, curr) => {
let item = acc.find(x => x.version == curr["Version Name"]);
if(!item){
item = {version: curr["Version Name"], zones:{}}
acc.push(item);
}
item.zones[curr.Zone] = (item.zones[curr.Zone] || 0) + curr.Value
return acc;
},[])
.map(x => ({
"Version Name": x.version,
"Zone Count": Object.keys(x.zones).length,
"Value Sum": Object.values(x.zones).reduce( (a,b) => a+b ,0)
}))
console.log(result);
First filter out, then group, then count the zones, for that we need some heler functions on arrays:
Array.prototype.groupBy = function groupBy(key) {
const hash = {}, result = [];
for(const el of this) {
if(hash[ el[key] ]) {
hash[ el[key] ].push(el);
} else {
result.push({
key: el[key],
values: hash[ el[key] ] = [ el ],
});
}
}
return result;
};
Array.prototype.key = function(key) {
return this.map(el => el[key]);
};
Array.prototype.sum = function(key) {
return this.reduce((total, el) => total + (key ? el[key] : el), 0);
};
Array.prototype.unique = function() {
return [...new Set(this)];
};
That is actually quite a lot of code, but now we can use that to build up our result:
const result = array
.filter(el => el.Selected)
.groupBy("Version Name")
.map(({ key, values }) => ({
"Version Name": key,
"Value Sum": values.sum("Sum"),
"Zone Count":values.key("Zone").unique().length,
}));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With