Group array by year and month

Viewed 137

I would like to group an array with events by year and month. My data looks like this:

const events = [
  {
    name: "event 1",
    year: 2021,
    month: 1,
  },
  {
    name: "event 2",
    year: 2021,
    month: 9,
  },
  {
    name: "event 3",
    year: 2021,
    month: 1,
  },
  {
    name: "event 4",
    year: 2022,
    month: 7,
  },
]

And my expected outcome should be something like this:

[
  {
    year: 2021,
    month: 1,
    events: [
      {
        name: "event 1"
      },
      {
        name: "event 3"
      }
    ]
  },
  {
    year: 2021,
    month: 9,
    events: [
      {
        name: "event 2"
      }
    ]
  }
]

What would be the best approach to do this? I found a couple stackoverflow posts to group an array by it's key value but that not what I'm looking for.

const groupBy = (array, key) => {
  return array.reduce((result, currentValue) => {
    // If an array already present for key, push it to the array. Else create an array and push the object
    (result[currentValue[key]] = result[currentValue[key]] || []).push(currentValue);
    // Return the current iteration `result` value, this will be taken as next iteration `result` value and accumulate
    return result;
  }, {}); // empty object is the initial value for result object
};

const groupedByYear = groupBy(events, 'year');
2 Answers

You can do this with reduce and Object.values

const events = [
  {
    name: "event 1",
    year: 2021,
    month: 1,
  },
  {
    name: "event 2",
    year: 2021,
    month: 9,
  },
  {
    name: "event 3",
    year: 2021,
    month: 1,
  },
];

const result = Object.values(events.reduce( (acc,evt) => {
    const key = `${evt.year}-${evt.month}`;
    if(!acc[key]) {
      acc[key] = {year: evt.year, month: evt.month, events:[]}
    }
    acc[key].events.push( {name:evt.name} );
    return acc;
},{}));

console.log(result);

You could take a dynamic approach by using a combined key for wanted properties for grouping.

Then remove all keys of grouing and push a new object without unwanted properties.

const
    events = [{ name: "event 1", year: 2021, month: 1 }, { name: "event 2", year: 2021, month: 9 }, { name: "event 3",  year: 2021, month: 1 }],
    keys = ['year', 'month'],
    result = Object.values(events.reduce((r, o) => {
        let value,
            key = keys.map(k => o[k]).join('|');

        if (!r[key]) r[key] = { ...Object.fromEntries(keys.map(k => [k, o[k]])), events: [] };
        
        r[key].events.push(keys.reduce((t, k) => (({ [k]: value, ...t } = t), t), o));
        return r;
    }, {}));

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related