Reputation: 6928
Let me explain what I'm talking about. I have the following array:
const days = [
{
date: '2016-12-13T00:00:00.000Z',
stats: [
{ name: 'A', sold: 34, },
{ name: 'B', sold: 3, },
{ name: 'C', sold: 26, },
],
},
{
date: '2016-12-14T00:00:00.000Z',
stats: [
{ name: 'D', sold: 34, },
{ name: 'E', sold: 3, },
{ name: 'F', sold: 26, },
],
},
{
date: '2016-12-14T00:00:00.000Z',
stats: [
{ name: 'D', sold: 34, },
{ name: 'E', sold: 3, },
{ name: 'F', sold: 26, },
],
},
];
What I'm trying to do is find the percentage of each stat.name
so for example. If we combine all sold values we get: 189
now find the %
of stat name B
(3 / 189) * 100
Which will give me: 1.58%
this represents the %
of sold items for the category.
Ideally the result I'm after would look like this:
const result = [
{ name: 'A', sold: 34, percentage: '17,98%' },
{ name: 'B', sold: 3, percentage: '1,58%', },
{ name: 'C', sold: 26, percentage: '13,75%', },
{ name: 'D', sold: 68, percentage: '35,97%', },
{ name: 'E', sold: 6, percentage: '3,17%' },
{ name: 'F', sold 52, percentage: '27,51%' },
];
What I did so far:
const days = [
{
date: '2016-12-13T00:00:00.000Z',
stats: [
{ name: 'A', sold: 34, },
{ name: 'B', sold: 3, },
{ name: 'C', sold: 26, },
],
},
{
date: '2016-12-14T00:00:00.000Z',
stats: [
{ name: 'D', sold: 34, },
{ name: 'E', sold: 3, },
{ name: 'F', sold: 26, },
],
},
{
date: '2016-12-14T00:00:00.000Z',
stats: [
{ name: 'D', sold: 34, },
{ name: 'E', sold: 3, },
{ name: 'F', sold: 26, },
],
},
];
let total = 0;
days.map(record => record.stats.map(category => total += category.sold)); // save the total;
const newStats = days.reduce(function (pastDay, currentDay) {
const nextStats = currentDay.stats.map(function(stat) {
const oldSold = pastDay.stats.find((old) => old.name === stat.name); // object that match by name.
let newSold;
if (oldSold) { // if matched
newSold = stat.sold + oldSold.sold // sum
} else { // don't sum anything
newSold = stat.sold
}
stat.sold = newSold;
stat.percentage = `${(newSold / total * 100).toFixed(2)}%`;
return stat;
});
return {
stats: nextStats,
};
});
console.log(newStats);
Which outsputs:
{
"stats": [
{
"name": "D",
"sold": 68,
"percentage": "35.98%"
},
{
"name": "E",
"sold": 6,
"percentage": "3.17%"
},
{
"name": "F",
"sold": 52,
"percentage": "27.51%"
}
]
}
A, B, C. are gone..
Is there a better approach to the whole thing? I don't really like mapping and getting the total first, then working with the rest.. Is there a way to do it better? Thanks...
Upvotes: 2
Views: 81
Reputation: 135247
I would highly encourage you to add some generic functions to abstract away some of the complexity here. There are many ways to do that, but I'll leave that as an exercise for you. Maybe I'll update the answer later today if I have more time.
Here's an approach using Array.prototype.reduce
const days = [ { date: '2016-12-13T00:00:00.000Z', stats: [ { name: 'A', sold: 34, }, { name: 'B', sold: 3, }, { name: 'C', sold: 26, }, ], }, { date: '2016-12-14T00:00:00.000Z', stats: [ { name: 'D', sold: 34, }, { name: 'E', sold: 3, }, { name: 'F', sold: 26, }, ], }, { date: '2016-12-14T00:00:00.000Z', stats: [ { name: 'D', sold: 34, }, { name: 'E', sold: 3, }, { name: 'F', sold: 26, } ] } ]
const makeSalesReport = days => {
let {map,sum} = days
.reduce((acc, {stats}) => [...acc, ...stats], [])
.reduce(({map, sum}, {name, sold}) => ({
map: map.set(name, map.has(name) ? map.get(name) + sold : sold),
sum: sum + sold
}), {map: new Map(), sum: 0})
return Array.from(map, ([name, sold]) =>
({name, sold, percentage: sold / sum * 100}))
}
console.log(makeSalesReport(days))
Here's another approach nicely bundled up in a function using for-of
loops
const days = [ { date: '2016-12-13T00:00:00.000Z', stats: [ { name: 'A', sold: 34, }, { name: 'B', sold: 3, }, { name: 'C', sold: 26, }, ], }, { date: '2016-12-14T00:00:00.000Z', stats: [ { name: 'D', sold: 34, }, { name: 'E', sold: 3, }, { name: 'F', sold: 26, }, ], }, { date: '2016-12-14T00:00:00.000Z', stats: [ { name: 'D', sold: 34, }, { name: 'E', sold: 3, }, { name: 'F', sold: 26, } ] } ]
const makeSalesReport = days => {
let map = new Map(), sum = 0
for (let {stats} of days) {
for (let {name, sold} of stats) {
map.set(name, map.has(name) ? map.get(name) + sold : sold),
sum += sold
}
}
return Array.from(map, ([name, sold]) =>
({name, sold, percentage: sold / sum * 100}))
}
console.log(makeSalesReport(days))
Upvotes: 1
Reputation: 386654
You could first collect the sold count and then render the array with the percentage.
var days = [{ date: '2016-12-13T00:00:00.000Z', stats: [{ name: 'A', sold: 34, }, { name: 'B', sold: 3, }, { name: 'C', sold: 26, }, ], }, { date: '2016-12-14T00:00:00.000Z', stats: [{ name: 'D', sold: 34, }, { name: 'E', sold: 3, }, { name: 'F', sold: 26, }, ], }, { date: '2016-12-14T00:00:00.000Z', stats: [{ name: 'D', sold: 34, }, { name: 'E', sold: 3, }, { name: 'F', sold: 26, }, ], }, ],
temp = Object.create(null),
result = [],
total = 0;
days.forEach(function (day) {
day.stats.forEach(function (stat) {
total += stat.sold;
temp[stat.name] = (temp[stat.name] || 0) + stat.sold;
});
}, Object.create(null));
result = Object.keys(temp).map(function (k) {
return { name: k, sold: temp[k], percentage: (temp[k] * 100 / total).toFixed(2) + '%' };
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Upvotes: 3
Reputation: 191986
Use Array#map
with Object#assign
, and array spread to combine all stats to a single array. Use Array#reduce
to get the sum. Map the flatlist
, and assign the percentage to each stat using a template literal:
const days = [{"date":"2016-12-13T00:00:00.000Z","stats":[{"name":"A","sold":34},{"name":"B","sold":3},{"name":"C","sold":26}]},{"date":"2016-12-14T00:00:00.000Z","stats":[{"name":"D","sold":34},{"name":"E","sold":3},{"name":"F","sold":26}]},{"date":"2016-12-14T00:00:00.000Z","stats":[{"name":"D","sold":34},{"name":"E","sold":3},{"name":"F","sold":26}]}];
// flatten the lists
const flatList = [].concat([], ...days.map(({ stats }) => stats ));
// get the sum
const sum = flatList.reduce(( sum, { sold }) => sum + sold, 0);
// assign the percentage to each
const result = flatList.map((stat) => Object.assign({}, stat, { percentage: `${(stat.sold / sum * 100).toFixed(2)}%` }));
console.log(result);
Upvotes: 1