delv123
delv123

Reputation: 146

Group array by two different keys and sum values

I'm working with an array on React and i'm trying to filter it by month and year, i managed to do it by month but for some reason i can't add the year key, this is what i have so far:

This is the array that i have originally:

paid = [
 {amount:155, month:11, year:2020, date:11-11-2020}
 {amount:120, month:11, year:2021, date:05-11-2021}
 {amount:135, month:12, year:2020, date:11-12-2020}
...
]

const group = groupBy(d, (invoices) => invoices.month); //d is the array called "paid"

This is the groupBy function:

function groupBy(list, keyGetter) {
    const map = new Map();
    list.forEach((item) => {
      const key = keyGetter(item);
      const collection = map.get(key);
      if (!collection) {
        map.set(key, [parseInt(item.amount)]);
      } else {
        collection.push(parseInt(item.amount));
      }
    });
    return map;
  }

And this is the result i have:

grouped = [
 {name:11, values: [155,120...]},
 {name:12, values: [135...]
];

And what i want to to is to also have it grouped by the year, so for example, the month 11, shouldn't have two values, because on the original array i have on month that's 11 from 2020 and one from 2021, so what i want to have as a result is this:

grouped = [
 {name:11/2020, values: [155...]},
 {name:11/2021, values: [120...]},
 {name:12/2020, values: [135...]
];

Can anyone help me with this?

Upvotes: 0

Views: 1468

Answers (2)

captain hak
captain hak

Reputation: 912

Working with Array.reduce method could be more simple, for the example I added some values:

const paid = [
 {amount:155, month:11, year:2020, date:'11-11-2020'},
 {amount:160, month:11, year:2020, date:'11-11-2020'},
 {amount:120, month:11, year:2021, date:'05-11-2021'},
 {amount:130, month:11, year:2021, date:'05-11-2021'},
 {amount:135, month:12, year:2020, date:'11-12-2020'},
 {amount:145, month:12, year:2020, date:'11-12-2020'}
]
const grouped = paid.reduce((acc,val)=>{
    if(acc[val.month+'/'+val.year]){
        acc[val.month+'/'+val.year].push(val.amount)
    } else {
        acc[val.month+'/'+val.year] = [val.amount]
    }
    return acc
}, {})
console.log(JSON.stringify(grouped,null,2))

EDIT -----------------
I edited the code to use the group by function and to produce an array containing the values and the sum. you can group passing an array containing first level key (like ['month'] or ['month', 'year']):

const paid = [
  { amount: 155, month: 11, year: 2020, date: "11-11-2020" },
  { amount: 160, month: 11, year: 2020, date: "11-11-2020" },
  { amount: 120, month: 11, year: 2021, date: "05-11-2021" },
  { amount: 130, month: 11, year: 2021, date: "05-11-2021" },
  { amount: 135, month: 12, year: 2020, date: "11-12-2020" },
  { amount: 145, month: 12, year: 2020, date: "11-12-2020" }
];

const groupBy = (data, keys) => {
  return Object.values(
    data.reduce((acc, val) => {
      const name = keys.reduce((finalName,key)=> finalName + val[key]+'/','').slice(0, -1)
      if (acc[name]) {
        acc[name].values.push(val.amount);
        acc[name].sum += val.amount;
      } else {
        acc[name] = {
          name,
          sum:val.amount,
          values:[val.amount]
        };;
      }
      return acc;
    }, {})
  );
};

console.log(JSON.stringify(groupBy(paid, ['month','year']), null, 2));

Upvotes: 1

Erik
Erik

Reputation: 327

If I understand correctly you want to sum all values per month per year. I guess this will work.

// Generate random data.
const genData = () => {
  const d = [];
  for(let i =0; i < 1000; i++) {
    d.push({
      year: Math.round(Math.random() * 10) + 2001,
      month: Math.round(Math.random() * 12),
      amount: Math.round(Math.random() * 100) + 100
    })
  }
  return d;
};

// Sum all data per month per year
const sumData = (d) => {
  const summed = {};
  for(const {year,month,amount} of d) {
    // By using a unique key for each year/month combi you can easily access the
    // intermedeiate result and add another value.
    const key = year + '/' + month;
    
    // If it does not yet exist, create an empty record for this year/month
    if(!(key in summed)) {
      summed[key] = {year,month, sum:0, values:[]};
    }
    
    // Grab the intermediate values and add amount to sum and to values
    summed[key].values.push(amount)
    summed[key].sum += amount;
  }
  return Object.values(summed);
};

// Run all
const d = genData();
console.log(d);
console.log(sumData(d));

Upvotes: 1

Related Questions