Grouping your data by calculated values

Find out how to do custom grouping by calculated values
When grouping data, most often, your group values are already in the data-set. However, there are some cases when your grouping logic is more complex than simply using existing fields in the data-set.
You might want to group by a computed field, or by using 2 fields at once.
In this article, we're going to use the olympic records data-set, which displays records about olympic medals - athlete name, country, year, season (winter or summer) and medal type.
Instead of grouping by year and then season, we want to group by the olympic game edition: eg: summer 2016. So effectively we need to group by year and season in the same group. Instead of heaving 2 grouping criteria, we'll have a single criteria.
<year,season>
In addition, for second and third level grouping, lets group by and then by .
Medal
Team
Defining the computed group value#
Play time ahead, so let's dive into the code. We'll demo how you can have custom computed values in both AG Grid and Infinite Table.
In AG Grid we achieve grouping by a computed value by using the column property. This let's you define the key to be used when grouping.
keyCreator
Grouping in AG Grid
View Mode
Fork Forkimport { AgGridReact } from "ag-grid-react"; import data, { type OlympicRecord } from "@thedatagrid/data/olympics_10k"; import { AllEnterpriseModule, ColDef, ModuleRegistry, themeQuartz, colorSchemeDark, } from "ag-grid-enterprise"; ModuleRegistry.registerModules([AllEnterpriseModule]); // Column definitions const columnDefs: ColDef<OlympicRecord>[] = [ { field: "Name", headerName: "Name" }, { field: "Medal", headerName: "Medal", rowGroupIndex: 1 }, { field: "Sport", headerName: "Sport" }, { field: "Team", headerName: "Team", rowGroupIndex: 2 }, { field: "Year", headerName: "Year" }, { field: "Season", headerName: "Olympic Season", keyCreator: (params) => `${params.data.Year}-${params.data.Season}`, valueFormatter: (params) => { return `${params.node?.key}`; }, rowGroupIndex: 0, }, { field: "City", headerName: "City" }, ]; export default function () { return ( <AgGridReact columnDefs={columnDefs} rowData={data} theme={themeQuartz.withPart(colorSchemeDark)} /> ); }
To specify the grouping order, we leverage in the column definitions.
rowGroupIndex
const columnDefs = [ { field: "Name", headerName: "Name" }, { field: "Medal", headerName: "Medal", rowGroupIndex: 1 }, { field: "Sport", headerName: "Sport" }, { field: "Team", headerName: "Team", rowGroupIndex: 2 }, { field: "Year", headerName: "Year" }, { field: "Season", rowGroupIndex: 0, headerName: "Olympic Season", keyCreator: (params) => `${params.data.Year}-${params.data.Season}`, valueFormatter: (params) => { return `${params.node?.key}`; }, }, { field: "City", headerName: "City" }, ];
COPY
Otherwise, the column order would determine the grouping order, which we don't want.
For comparison, let's have a look at Infinite Table.
Infinite Table has a data-first approach, and the grouping is defined on the component and is not neccessarily bound to a column.
<DataSource />
In order to define grouping in Infinite Table, you have to define a array, which lists the grouping criteria, in order.
groupBy
For the computed grouping, you define a for the custom group key. All other groups are simply bound to fields.
valueGetter
Grouping in Infinite Table
View Mode
Fork Forkimport "@infinite-table/infinite-react/index.css"; import { InfiniteTable, DataSource, InfiniteTableColumn, DataSourceGroupBy, DataSourceProps, } from "@infinite-table/infinite-react"; import data, { type OlympicRecord } from "@thedatagrid/data/olympics_10k"; const columns: Record<string, InfiniteTableColumn<OlympicRecord>> = { Name: { field: "Name" }, Medal: { field: "Medal" }, Sport: { field: "Sport" }, Team: { field: "Team" }, Year: { field: "Year" }, Season: { field: "Season" }, City: { field: "City" }, }; const defaultGrouping: DataSourceGroupBy<OlympicRecord>[] = [ { field: "Season", valueGetter: (row) => `${row.data.Year}-${row.data.Season}`, }, { field: "Medal", }, { field: "Team", }, ]; const groupRowsState: DataSourceProps<OlympicRecord>["groupRowsState"] = { expandedRows: [], collapsedRows: true, }; export default function () { return ( <DataSource primaryKey={"Id"} defaultGroupRowsState={groupRowsState} data={data} defaultGroupBy={defaultGrouping} > <InfiniteTable columns={columns} groupRenderStrategy="single-column" /> </DataSource> ); }
By default, Infinite Table does not show the group count.
Customizing the group column & row rendering#
In the examples above, notice how Infinite Table does not show a group count. To do that, you have to customize the rendering of the in order to include that. The snippet below shows how you can achieve this.
groupColumn
Customizing the group column rendering in Infinite Table
const groupColumn = { defaultWidth: 220, renderValue: (params) => { if (params.isGroupRow) { return `${params.value} (${params.rowInfo.groupCount})`; } return params.value; }, };
COPY
Now let's try and style the group row.
In Infinite Table, we can use the function.
rowStyle
Row styling
In Infinite Table, the
prop can either be an object or a function.
rowStyle
rowStyle={({ rowInfo }) => {
if (rowInfo.isGroupRow) {
return {
backgroundColor: "#9b6604",
};
}
}}
COPY
In addition, let's render all groups expanded.
Grouping in Infinite Table
View Mode
Fork Forkimport "@infinite-table/infinite-react/index.css"; import { InfiniteTable, DataSource, InfiniteTableColumn, DataSourceGroupBy, DataSourceProps, } from "@infinite-table/infinite-react"; import data, { type OlympicRecord } from "@thedatagrid/data/olympics_10k"; const columns: Record<string, InfiniteTableColumn<OlympicRecord>> = { Name: { field: "Name" }, Medal: { field: "Medal" }, Sport: { field: "Sport" }, Team: { field: "Team" }, Year: { field: "Year" }, Season: { field: "Season" }, City: { field: "City" }, }; const defaultGrouping: DataSourceGroupBy<OlympicRecord>[] = [ { field: "Season", valueGetter: (row) => `${row.data.Year}-${row.data.Season}`, }, { field: "Medal", }, { field: "Team", }, ]; const groupColumn: InfiniteTableColumn<OlympicRecord> = { defaultWidth: 220, renderValue: (params) => { if (params.isGroupRow) { return `${params.value} (${params.rowInfo.groupCount})`; } return params.value; }, }; export default function () { return ( <DataSource primaryKey={"Id"} data={data} defaultGroupBy={defaultGrouping}> <InfiniteTable columns={columns} groupRenderStrategy="single-column" groupColumn={groupColumn} rowStyle={({ rowInfo }) => { if (rowInfo.isGroupRow) { return { backgroundColor: "#9b6604", }; } }} /> </DataSource> ); }
Let's do the same in AG Grid. AG Grid exposes rowStyle object and also the getRowStyle function to add styles dynamically.
To expand everything we'll add the prop.
groupDefaultExpanded=-1
Adding styles to your custom AG Grid grouping
View Mode
Fork Forkimport { AgGridReact } from "ag-grid-react"; import data, { type OlympicRecord } from "@thedatagrid/data/olympics_10k"; import { AllEnterpriseModule, ColDef, colorSchemeDark, ModuleRegistry, themeQuartz, } from "ag-grid-enterprise"; ModuleRegistry.registerModules([AllEnterpriseModule]); // Column definitions const columnDefs: ColDef<OlympicRecord>[] = [ { field: "Name", headerName: "Name" }, { field: "Medal", headerName: "Medal", rowGroupIndex: 1 }, { field: "Sport", headerName: "Sport" }, { field: "Team", headerName: "Team", rowGroupIndex: 2 }, { field: "Year", headerName: "Year" }, { field: "Season", headerName: "Olympic Season", keyCreator: (params) => `${params.data.Year}-${params.data.Season}`, valueFormatter: (params) => { return `${params.node?.key}`; }, rowGroupIndex: 0, }, { field: "City", headerName: "City" }, ]; export default function () { return ( <AgGridReact columnDefs={columnDefs} rowData={data} theme={themeQuartz.withPart(colorSchemeDark)} groupDefaultExpanded={-1} getRowStyle={({ node }) => { if (node.group) { return { backgroundColor: "#9b6604", }; } }} /> ); }
Conclusions#
This sums up some of the lesser known functionalities about grouping on a computed value.
In addition to that, this article shows how you can easily achieve custom expand/collapse states for group rows and custom styling.
It also gives you an idea of how AG Grid and Infinite Table compare when specifying your grouping definitions.
We hope you've found this useful.
For more content like this, follow us on at @thedatagrid