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
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);
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'},
];
}
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'},
];
}