I'm trying to create a stacked bar graph where I count the categories per month
The data coming from the backend is similar to this:
[
{date: 'January', category: 'A'},
{date: 'January', category: 'B'},
{date: 'February', category: 'A'},
{date: 'February', category: 'B'},
{date: 'February', category: 'B'},
{date: 'March', category: 'C'},
{date: 'March', category: 'A'},
{date: 'April', category: 'C'},
...
]
With help, I've managed to count the different categories and group the data into their respective months like the shape below:
[
{date: 'January', countA: 1, countB: 1, countC: 0},
{date: 'February', countA: 1, countB: 2, countC: 0},
{date: 'March', countA: 1, countB: 0, countC: 1},
...
]
However, this only produces the data whenever the month exists. So if there is no data for the month of, say, June, there won't be any object with the name: 'June'
.
I need to have these missing values filled with the corresponding month and with the count values to zeros
CodePudding user response:
Once data is ready with missing months only, fill in missing entries with Array.map
const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
const dataWithMissingMonths = [ {date: 'January', countA: 1, countB: 1, countC: 0}, {date: 'February', countA: 1, countB: 2, countC: 0}, {date: 'March', countA: 1, countB: 0, countC: 1} ];
const result = months.map(m => dataWithMissingMonths.find(data => data.date === m)?? {date: m, countA: 0, countB: 0, countC: 0});
console.log(result);
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
CodePudding user response:
Here is an approach that is pretty straightforward, using forEach
.
More on forEach here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
let months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
let cleanData =[];
let dirtyData = [];
// get original data
dirtyData = getData();
// iterate through each month
months.forEach(month => {
let monthEntries = dirtyData.filter( e => e.date == month);
let aCount = 0;
let bCount = 0;
let cCount = 0;
// for each month, count how many enteries there were in each category
monthEntries.forEach(e => {
switch (e.category.toLowerCase()){
case "a":
aCount ;
break;
case "b":
bCount ;
break;
case "c":
cCount ;
break;
}
});
// push the data to a new cleanData array in our preferred format
cleanData.push({name: month, countA: aCount, countB: bCount, countC: cCount})
});
// log results
console.log(cleanData);
// function to retreive original data
function getData() { return [
{date: 'January', category: 'A'},
{date: 'January', category: 'B'},
{date: 'February', category: 'A'},
{date: 'February', category: 'B'},
{date: 'February', category: 'C'},
{date: 'March', category: 'B'},
{date: 'March', category: 'A'},
{date: 'March', category: 'B'},
{date: 'March', category: 'C'},
{date: 'March', category: 'B'},
{date: 'April', category: 'A'},
{date: 'April', category: 'B'},
{date: 'April', category: 'C'},
{date: 'April', category: 'B'},
{date: 'May', category: 'A'},
{date: 'May', category: 'B'},
{date: 'May', category: 'C'},
{date: 'May', category: 'B'},
{date: 'May', category: 'A'},
{date: 'May', category: 'B'},
{date: 'May', category: 'C'},
{date: 'May', category: 'B'},
{date: 'May', category: 'A'},
{date: 'May', category: 'B'},
{date: 'June', category: 'C'},
{date: 'June', category: 'B'},
{date: 'June', category: 'A'},
{date: 'July', category: 'B'},
{date: 'July', category: 'C'},
{date: 'July', category: 'B'},
{date: 'July', category: 'A'},
{date: 'July', category: 'B'},
{date: 'August', category: 'C'},
{date: 'September', category: 'B'},
{date: 'September', category: 'A'},
{date: 'September', category: 'B'},
{date: 'October', category: 'C'},
{date: 'October', category: 'B'},
{date: 'October', category: 'A'},
{date: 'October', category: 'B'},
{date: 'November', category: 'C'},
{date: 'November', category: 'A'},
{date: 'December', category: 'B'},
{date: 'December', category: 'B'},
];
}
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
CodePudding user response:
Here is an alternate implementation using reduce:
let arr = getData();
let res = Object.values(arr.reduce((acc, {date, category})=>{
(acc[date] ??= {name: date, countA: 0, countB: 0, countC: 0})['count' category];
return acc;
}, {}));
console.log(res);
function getData() { return [
{date: 'January', category: 'A'},
{date: 'January', category: 'B'},
{date: 'February', category: 'A'},
{date: 'February', category: 'B'},
{date: 'February', category: 'C'},
{date: 'March', category: 'B'},
{date: 'March', category: 'A'},
{date: 'March', category: 'B'},
{date: 'March', category: 'C'},
{date: 'March', category: 'B'},
{date: 'April', category: 'A'},
{date: 'April', category: 'B'},
{date: 'April', category: 'C'},
{date: 'April', category: 'B'},
{date: 'May', category: 'A'},
{date: 'May', category: 'B'},
{date: 'May', category: 'C'},
{date: 'May', category: 'B'},
{date: 'May', category: 'A'},
{date: 'May', category: 'B'},
{date: 'May', category: 'C'},
{date: 'May', category: 'B'},
{date: 'May', category: 'A'},
{date: 'May', category: 'B'},
{date: 'June', category: 'C'},
{date: 'June', category: 'B'},
{date: 'June', category: 'A'},
{date: 'July', category: 'B'},
{date: 'July', category: 'C'},
{date: 'July', category: 'B'},
{date: 'July', category: 'A'},
{date: 'July', category: 'B'},
{date: 'August', category: 'C'},
{date: 'September', category: 'B'},
{date: 'September', category: 'A'},
{date: 'September', category: 'B'},
{date: 'October', category: 'C'},
{date: 'October', category: 'B'},
{date: 'October', category: 'A'},
{date: 'October', category: 'B'},
{date: 'November', category: 'C'},
{date: 'November', category: 'A'},
{date: 'December', category: 'B'},
{date: 'December', category: 'B'},
];
}
<iframe name="sif3" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>