Skip to main content
views

Typing a group by year reduce function with generics

In my most recent rebuild of my personal site here, I decided I wanted to enable strict TS and group content by years on their respective pages.

To start, here is the reducer function that takes in an array of data and groups items by their year.

js
const groupByYear = (arr) =>
arr.reduce((acc, current) => {
const year = new Date(current.date).getFullYear().toString();
if (acc[year] !== undefined) {
acc[year].push(current);
} else {
acc[year] = [current];
}
return acc;
});

My site has a few different interfaces for data. For example posts have title, description, date, and tags where as the feed only has date and tags.

So to make use of this function and have types I needed to make use of generic.

ts
export const groupByYear = <
// Take in some type of object as T that
// we expect to have an included date property.
T extends {
date: string;
},
>(
// Not we use the generic to define the expected
// data being passed in.
arr: Array<T>,
) =>
arr.reduce((acc, current) => {
const year = new Date(current.date).getFullYear().toString();
if (acc[year] !== undefined) {
acc[year].push(current);
} else {
acc[year] = [current];
}
return acc;
// Record the return type to be an object of keys
// as strings, and an array of our generic T.
}, {} as Record<string, Array<T>>);

Usage

Lets create some sample data JSON.

json
[
{
"date": "2021-01-03",
"heading": "Heading Sample One"
},
{
"date": "2021-12-24",
"heading": "Heading Sample Two"
},
{
"date": "2020-04-28",
"heading": "Heading Sample Three"
}
]

Will now import that data JSON, create our interface as IData and use that interface as our generic type for our groupByYear function.

ts
import data from './data.json'
import groupByYear from './groupByYear'
interface IData {
date: string
heading: string
}
const dataGroupedByYear = groupByYear<IData>(data);
console.log(dataGroupedByYear) // {2020: Array(1), 2021: Array(2)}