Home > Net >  How to group and count an array of objects
How to group and count an array of objects

Time:11-12

I am trying to create a stacked bar graph, but I can't figure out how to reshape my data.

The data coming from the backend looks something like below, where it has a date and some category key

[
    {date: 'January', category: 'A'},
    {date: 'January', category: 'B'},
    {date: 'February', category: 'A'},
    {date: 'February', category: 'B'},
    {date: 'February', category: 'C'},
    {date: 'February', category: 'B'},
    ...
]

I need to reshape this data into the form like below, where it will group them into their respective months, and then count the number of times each category was seen

[
    {name: 'January', countA: 1, countB: 1, countC: 0 },
    {name: 'February', countA: 1, countB: 2, countC: 1 }
    ...
]

So far, I've only been able to count the total number of categories per month using the reduce function such as

const filterData = data.reduce((groups, curr) => {
    const {January = 0, February = 0, ...} = groups;
    switch(curr.date){
        case 'January': return {...groups, January: January   1}
        case 'February': return {...groups, February: February   1}
        ...
    }
}, {})

But this won't work for the stacked bar graph.

CodePudding user response:

You can use Array#reduce with an object to store the counts for each month.

let arr = [
    {date: 'January', category: 'A'},
    {date: 'January', category: 'B'},
    {date: 'February', category: 'A'},
    {date: 'February', category: 'B'},
    {date: 'February', category: 'C'},
    {date: 'February', category: 'B'},
];
let res = Object.values(arr.reduce((acc, {date, category})=>{
      (acc[date] ??= {date, countA: 0, countB: 0, countC: 0})['count'   category];
    return acc;
}, {}));
console.log(res);
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

CodePudding user response:

Based on your question we can't really be sure there will be only three categories so here's a solution that will deal with n number of them. It's longer than you may want but hopefully not too verbose. (I've used a for...of loop instead of reduce at the end but you can swap them if you want.)

const data = [
  {date: 'March', category: 'Z'},
  {date: 'January', category: 'A'},
  {date: 'January', category: 'B'},
  {date: 'February', category: 'A'},
  {date: 'November', category: 'D'},
  {date: 'February', category: 'M'},
  {date: 'February', category: 'B'},
  {date: 'February', category: 'C'},
  {date: 'February', category: 'B'},
  {date: 'January', category: 'D'}
];

// Create a deduped array of elements with
// a `count[letter]` format by creating
// a Set of mapped categories
const catArr = new Set(data.map(obj => {
  return `count${obj.category}`;
}));

// Create a new counts object from the set
// setting each count property to 0
const counts = {};
for (const count of catArr) {
  counts[count] = 0;
}

// Iterate over the data. If the date key isn't
// on the output object, add it and set it where
// one of the properties is the date, and the others
// are a copy of the categories
const out = {};
for (const obj of data) {
  const { date, category } = obj;
  const count = `count${category}`;
  out[date] = out[date] || { name: date, ...counts };
    out[date][count];
}

// Get the values of the output object
console.log(Object.values(out));
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

CodePudding user response:

Since the other answers have covered approaches using reduce and for...of, I'll add another 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="sif3" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

  • Related