CxJS

Grouper

import { Grouper } from 'cx/data'; Copied

Grouper groups records by key fields and computes aggregate values like sum, average, count, min, and max.

<div controller={PageController}>
  <h4 style="margin: 0 0 8px">Sales Data</h4>
  <Grid
    records={m.sales}
    columns={[
      { header: "Region", field: "region" },
      { header: "Product", field: "product" },
      { header: "Amount", field: "amount", format: "currency", align: "right" },
      { header: "Qty", field: "quantity", align: "right" },
    ]}
  />

  <h4 style="margin: 16px 0 8px">Grouped by Region</h4>
  <Grid
    records={m.grouped}
    columns={[
      { header: "Region", field: "key.region" },
      {
        header: "Total Amount",
        field: "aggregates.totalAmount",
        format: "currency",
        align: "right",
      },
      { header: "Total Qty", field: "aggregates.totalQty", align: "right" },
      {
        header: "Avg Amount",
        field: "aggregates.avgAmount",
        format: "currency",
        align: "right",
      },
      { header: "Records", field: "records.length", align: "right" },
    ]}
  />
</div>

Sales Data

RegionProductAmountQty
NorthWidget$1005
NorthGadget$2003
SouthWidget$1507
SouthGadget$802
EastWidget$1204
EastGadget$906

Grouped by Region

RegionTotal AmountTotal QtyAvg AmountRecords
North$3008$1502
South$2309$1152
East$21010$1052

Constructor

new Grouper(key, aggregates?, dataGetter?, nameGetter?)

Parameters

ParameterTypeDescription
keyobjectObject mapping field names to value selectors.
aggregatesobjectOptional. Aggregate definitions.
dataGetterfunctionOptional. Extract data from record.
nameGetterfunctionOptional. Extract display name for group.

Aggregate Types

TypeDescription
sumSum of values.
avgAverage of values.
countCount of records.
minMinimum value.
maxMaximum value.
distinctCount of distinct values.

Methods

MethodDescription
process(record, index)Process a single record.
processAll(records)Process all records at once.
getResults()Get array of group results.
reset()Reset the grouper for reuse.

Examples

Basic grouping

const grouper = new Grouper({
  category: (item) => item.category,
});

grouper.processAll(items);
const groups = grouper.getResults();
// [{ key: { category: "A" }, records: [...], indexes: [...] }, ...]

With aggregates

const grouper = new Grouper(
  { region: (s) => s.region },
  {
    total: { type: "sum", value: (s) => s.amount },
    average: { type: "avg", value: (s) => s.amount },
    count: { type: "count" },
    minAmount: { type: "min", value: (s) => s.amount },
    maxAmount: { type: "max", value: (s) => s.amount },
  },
);

grouper.processAll(sales);
const results = grouper.getResults();
// [{
//   key: { region: "North" },
//   records: [...],
//   aggregates: { total: 300, average: 150, count: 2, minAmount: 100, maxAmount: 200 }
// }, ...]

Multi-level grouping

const grouper = new Grouper({
  year: (s) => s.date.getFullYear(),
  month: (s) => s.date.getMonth() + 1,
});

Weighted average

const grouper = new Grouper(
  { category: (item) => item.category },
  {
    weightedAvg: {
      type: "avg",
      value: (item) => item.price,
      weight: (item) => item.quantity,
    },
  },
);

Result Structure

interface GroupResult {
  key: object; // Group key values
  name?: any; // Optional display name
  records: any[]; // Records in this group
  indexes: number[]; // Original indexes
  aggregates?: object; // Computed aggregates
}

See Also