Group Data by Quarter
Group monthly financial data by calendar quarter in JavaScript for dashboards, reports, charting, and tax datasets.
Group List by Quarters
The input starts with monthly report rows and cumulative totals. The transformation groups them by calendar quarter so the result is easier to render in quarterly tables, charts, and report cards.
Useful for:
- Visualizing quarterly reports
- Aggregating financial or performance data
- Preparing charting datasets or grouped UI views
Steps
groupDataByQuarter(data)organizes report rows into keys like"2023-Q1"based on thedatefield.initializeDates(groupedQuarters)prepares a compact list of quarters and month strings.initializeRestData(groupedQuarters)turns grouped data into metric rows such asprofitandprofit_percent.
Keeping the steps separate makes the transformation easier to test and easier to adjust for a different report layout.
const getQuarter = (date) => Math.floor(date.getMonth() / 3) + 1;
const parseISO = (value) => new Date(`${value}T00:00:00`);
// data
export const TaxEntitiesKeys = {
profit: "profit",
profit_percent: "profit_percent",
};
const mockTaxes = {
cumulative: [
{ date: "2023-01-31", profit: 1000, profit_percent: 100 },
{ date: "2023-02-28", profit: 2000, profit_percent: 200 },
{ date: "2023-04-30", profit: 3000, profit_percent: 250 },
],
report: [
{ date: "2023-01-31", profit: 300, profit_percent: 30 },
{ date: "2023-02-28", profit: 500, profit_percent: 50 },
{ date: "2023-04-30", profit: 700, profit_percent: 70 },
],
};
// data
// group by quarters
const groupDataByQuarter = (data) => {
const quarters = {};
// Group data into quarters based on their date
for (const monthData of data.report) {
const date = parseISO(monthData.date);
const year = date.getFullYear();
const quarter = getQuarter(date);
const key = `${year}-Q${quarter}`;
if (!quarters[key]) {
quarters[key] = { months: [], total: null };
}
quarters[key].months.push(monthData);
}
// Assign cumulative totals to their respective quarters
for (const totalData of data.cumulative) {
const date = parseISO(totalData.date);
const year = date.getFullYear();
const quarter = getQuarter(date);
const key = `${year}-Q${quarter}`;
if (quarters[key]) {
quarters[key].total = totalData;
}
}
return quarters;
};
const groupedQuarters = groupDataByQuarter(mockTaxes);
console.log(JSON.stringify({ groupedQuarters }, null, 2));
// group by quarters
// {
// groupedQuarters: {
// "2023-Q1": {
// months: [
// {
// date: "2023-01-31",
// profit: 300,
// profit_percent: 30,
// },
// {
// date: "2023-02-28",
// profit: 500,
// profit_percent: 50,
// },
// ],
// total: {
// date: "2023-02-28",
// profit: 2000,
// profit_percent: 200,
// },
// },
// "2023-Q2": {
// months: [
// {
// date: "2023-04-30",
// profit: 700,
// profit_percent: 70,
// },
// ],
// total: {
// date: "2023-04-30",
// profit: 3000,
// profit_percent: 250,
// },
// },
// },
// };
// dates
const initializeDates = (quarters) => {
const dates = [];
Object.entries(quarters).forEach(([dateKey, data]) => {
const [year, quarter] = dateKey.split("-Q").map(Number);
let yearEntry = dates.find((entry) => entry.year === year);
if (!yearEntry) {
yearEntry = { year, periods: [] };
dates.push(yearEntry);
}
yearEntry.periods.push({
months: data.months.map((month) => month.date),
quarter: `${quarter}`,
});
});
return dates;
};
console.log(
JSON.stringify({ dates: initializeDates(groupedQuarters) }, null, 2),
);
// dates
// {
// dates: [
// {
// year: 2023,
// periods: [
// {
// months: ["2023-01-31", "2023-02-28"],
// quarter: "1",
// },
// {
// months: ["2023-04-30"],
// quarter: "2",
// },
// ],
// },
// ],
// };
// rest data
const initializeRestData = (quarters) => {
return Object.keys(TaxEntitiesKeys).map((taxKey) => {
const taxKeyTyped = taxKey;
return {
title: taxKeyTyped,
periods: Object.entries(quarters).map(([_, data]) => ({
months: data.months.map((month) => month[taxKeyTyped]),
total: data.total ? data.total[taxKeyTyped] : null,
})),
};
});
};
console.log(
JSON.stringify({ restData: initializeRestData(groupedQuarters) }, null, 2),
);
// rest data
// {
// restData: [
// {
// title: "profit",
// periods: [
// {
// months: [300, 500],
// total: 2000,
// },
// {
// months: [700],
// total: 3000,
// },
// ],
// },
// {
// title: "profit_percent",
// periods: [
// {
// months: [30, 50],
// total: 200,
// },
// {
// months: [70],
// total: 250,
// },
// ],
// },
// ],
// };