LyteNyte Grid logo for light mode. Links back to the documentation home page.
Client Row Source

Client Row Aggregations

LyteNyte Grid's client data source allows you to aggregate grouped-row data to produce values for display at the group level.

Info

This guide assumes you are familiar with grouping rows using the client data source. See the Client Row Grouping guide if not.

Aggregations

In LyteNyte Grid, an aggregation is a record of key-value pairs, where each key is usually a column id, and each value is the result of an aggregation function such as sum or average.

This concept of an aggregation is reflected in the data type required by a row group node and row aggregation node. These nodes must use object data, unlike leaf rows, which may use any data type.

To compute an aggregation using the client data source, pass one of the following to the useClientDataSource:

  • An aggregation function
  • A set of aggregation dimensions

Aggregation Functions

An aggregation function receives a set of leaf rows to aggregate. The function must return an object containing all computed aggregation values.

In the demo below, the aggregation function computes the count of unique values for text columns and the average for number columns. The demo passes this aggregation function to the aggregate property of the useClientDataSource hook.

Function Aggregate Rows

Fork code on stack blitzFork code on code sandbox

The aggregation function performs the full aggregation for all columns in the grid. The code below shows an example implementation. Notice how this logic can become repetitive. For this reason, LyteNyte Grid provides aggregation dimensions to simplify computing aggregations in a more declarative way.

const aggFn: Grid.T.AggregationFn<GridSpec["data"]> = (data) => {
const job = uniq(data.map((x) => x.data.job)).length === 1 ? data[0].data.job : null;
const age = sum(data.map((x) => x.data.age)) / data.length;
const balance = sum(data.map((x) => x.data.balance)) / data.length;
const education = uniq(data.map((x) => x.data.education)).length === 1 ? data[0].data.education : null;
const marital = uniq(data.map((x) => x.data.marital)).length === 1 ? data[0].data.marital : null;
const housing = uniq(data.map((x) => x.data.housing)).length === 1 ? data[0].data.housing : null;
const default_ = uniq(data.map((x) => x.data.default)).length === 1 ? data[0].data.default : null;
const loan = uniq(data.map((x) => x.data.loan)).length === 1 ? data[0].data.loan : null;
const contact = uniq(data.map((x) => x.data.contact)).length === 1 ? data[0].data.contact : null;
const day = uniq(data.map((x) => x.data.day)).length === 1 ? data[0].data.day : null;
const month = uniq(data.map((x) => x.data.month)).length === 1 ? data[0].data.month : null;
const duration = uniq(data.map((x) => x.data.duration)).length === 1 ? data[0].data.duration : null;
14 collapsed lines
return {
job,
age,
housing,
balance,
education,
marital,
default: default_,
loan,
contact,
day,
month,
duration,
};
};

Aggregation Dimensions

An aggregation dimension is an object with a dim property that contains an id value and optionally a field, and an fn property that references an aggregator function. The full type is shown below:

export type DimensionAgg<T> = {
dim: { id: string; field?: Field<T> };
fn: Aggregator<T> | string;
};

Here are some examples of aggregation dimensions:

const sumAgg = { dim: { id: "balance" }, fn: "sum" };
const avgAgg = { dim: { id: "avg", field: "balance" }, fn: "avg" };
const fnAgg = { dim: { id: "education" }, fn: SameFn };

The fn property is an Aggregator or a string that references a registered Aggregator function. An Aggregator function computes a single aggregated value. An Aggregator has the following type:

type PathField = { kind: "path"; path: string };
type Field<T> = string | number | PathField | ((params: { row: RowNode<T> }) => unknown);
export type Aggregator<T> = (field: Field<T>, data: RowLeaf<T>[]) => unknown;

Since the field parameter of an Aggregator can be a dynamic type, LyteNyte Grid exports the computeField utility function. computeField returns the value of the registered field for a given leaf row.

The computeField utility returns unknown by default. You can cast the return value by providing a generic parameter to the function. If you provide a type, you must ensure the returned value conforms to the specified type. The computeField utility will not validate the type for you.

// returns the value of the education property on the row's data
computeField<string>("education", row);

Note

Do not confuse Aggregators with the AggregationFn function you provide to the useClientDataSource hook. An Aggregator computes a single value that is assigned to a single key. An AggregationFn produces the full aggregation result object.

When an aggregation dimension uses a string fn, the client data source looks up the corresponding Aggregator from the grid’s registered aggregators. Register aggregators on the aggregateFns property of the grid. For example, you can define a summation aggregator as follows:

// Example shape only
const sum: Grid.T.Aggregator<BankData> = (field, data) => {
// ...
};

In the demo below, aggregation dimensions compute the aggregation value for each group row. The demo registers two aggregators ahead of time:

  • same: Returns a value if all aggregated values are the same; otherwise returns null.
  • avg: Returns the average of the aggregated values.

Client Row Aggregation Dimensions

Fork code on stack blitzFork code on code sandbox

Updating Aggregations

LyteNyte Grid does not enforce a predefined aggregation model. You are free to extend the grid’s API or column definitions to include one.

This section guides you on extending the column specification to allow columns to specify an aggregation and define a list of allowed aggregations. Aggregations encompass arbitrary computations over row-level data, including advanced statistical and windowed operations such as standard deviation and rolling averages.

Start by creating an updated column specification. In this example, the fields are mandatory, but you can choose a type that matches your use case.

interface GridSpec {
readonly data: BankData;
readonly column: {
agg: string;
allowedAggs: string[];
};
}

This example uses strings for aggregations, since the demo registers all possible aggregations ahead of time.

Text columns support four aggregators:

  • same
  • first
  • last
  • count

Number columns support seven aggregators as well:

  • avg
  • sum
  • count
  • max
  • min
  • first
  • last

In the demo below, the aggregators are created and added to the client data source. The column headers use a custom header renderer to let you change the aggregation applied to a column based on the allowedAggs list.

Row Group Aggregation Updates

Fork code on stack blitzFork code on code sandbox

The demo extends the columns with the agg and allowedAggs properties. The demo then derives aggregation dimensions from the columns to create the value passed to the useClientDataSource hook. The code below shows this derivation:

const aggModel = useMemo(() => {
return columns.map((x) => ({ dim: x, fn: x.agg }));
}, [columns]);

Next Steps