Documentation
Aggregate Functions
Aggregate functions automatically calculate summary values for grouped data in your tables. Simple Table supports built-in aggregations like sum, average, count, min, max, and custom functions to provide powerful data insights with automatic computation and formatting.
1import {SimpleTable} from "@simple-table/react";import type { Theme } from "@simple-table/react";2import { aggregateFunctionsConfig } from "./aggregate-functions.demo-data";3import "@simple-table/react/styles.css";45const AggregateFunctionsDemo = ({6 height = "400px",7 theme,8}: {9 height?: string | number;10 theme?: Theme;11}) => {12 return (13 <SimpleTable14 defaultHeaders={aggregateFunctionsConfig.headers}15 rows={aggregateFunctionsConfig.rows}16 rowGrouping={aggregateFunctionsConfig.tableProps.rowGrouping}17 columnResizing18 height={height}19 theme={theme}20 />21 );22};2324export default AggregateFunctionsDemo;
1<template>2 <SimpleTable3 :default-headers="aggregateFunctionsConfig.headers"4 :rows="aggregateFunctionsConfig.rows"5 :row-grouping="aggregateFunctionsConfig.tableProps.rowGrouping"6 :column-resizing="true"7 :height="height"8 :theme="theme"9 />10</template>1112<script setup lang="ts">13import {SimpleTable} from "@simple-table/vue";import type { Theme } from "@simple-table/vue";14import { aggregateFunctionsConfig } from "./aggregate-functions.demo-data";15import "@simple-table/vue/styles.css";1617withDefaults(defineProps<{ height?: string | number; theme?: Theme }>(), {18 height: "400px",19});20</script>
1import { Component, Input } from "@angular/core";2import {SimpleTableComponent} from "@simple-table/angular";import type { AngularHeaderObject, Row, Theme } from "@simple-table/angular";3import { aggregateFunctionsConfig } from "./aggregate-functions.demo-data";4import "@simple-table/angular/styles.css";56@Component({7 selector: "aggregate-functions-demo",8 standalone: true,9 imports: [SimpleTableComponent],10 template: `11 <simple-table12 [rows]="rows"13 [defaultHeaders]="headers"14 [rowGrouping]="grouping"15 [columnResizing]="true"16 [height]="height"17 [theme]="theme"18 ></simple-table>19 `,20})21export class AggregateFunctionsDemoComponent {22 @Input() height: string | number = "400px";23 @Input() theme?: Theme;2425 readonly rows: Row[] = aggregateFunctionsConfig.rows;26 readonly headers: AngularHeaderObject[] = aggregateFunctionsConfig.headers;27 readonly grouping = aggregateFunctionsConfig.tableProps.rowGrouping;28}293031// aggregate-functions.demo-data.ts32// Self-contained demo table setup for this example.33import type { AngularHeaderObject } from "@simple-table/angular";343536export const aggregateFunctionsHeaders: AngularHeaderObject[] = [37 { accessor: "name", label: "Name", width: 200, expandable: true, type: "string" },38 {39 accessor: "followers",40 label: "Followers",41 width: 120,42 type: "number",43 aggregation: { type: "sum" },44 valueFormatter: ({ value }) => {45 if (typeof value === "number") {46 return value >= 100000047 ? `${(value / 1000000).toFixed(1)}M`48 : value >= 100049 ? `${(value / 1000).toFixed(0)}K`50 : value.toString();51 }52 return "";53 },54 },55 {56 accessor: "revenue",57 label: "Monthly Revenue",58 width: 140,59 type: "string",60 aggregation: {61 type: "sum",62 parseValue: (value) => {63 if (typeof value !== "string") return 0;64 const numericValue = parseFloat(value.replace(/[$K]/g, ""));65 return isNaN(numericValue) ? 0 : numericValue;66 },67 },68 valueFormatter: ({ value }) => {69 if (typeof value === "number") return `$${value.toFixed(1)}K`;70 if (typeof value === "string") return value;71 return "";72 },73 },74 {75 accessor: "rating",76 label: "Rating",77 width: 100,78 type: "number",79 aggregation: { type: "average" },80 valueFormatter: ({ value }) => (typeof value === "number" ? `${value.toFixed(1)} ⭐` : ""),81 },82 {83 accessor: "contentCount",84 label: "Content",85 width: 90,86 type: "number",87 aggregation: { type: "sum" },88 },89 {90 accessor: "avgViewTime",91 label: "Avg Watch Time",92 width: 130,93 type: "number",94 aggregation: { type: "average" },95 valueFormatter: ({ value }) => (typeof value === "number" ? `${Math.round(value)}min` : ""),96 },97 { accessor: "status", label: "Status", width: 120, type: "string" },98];99100export const aggregateFunctionsData = [101 {102 id: 1,103 name: "StreamFlix",104 status: "Leading Platform",105 categories: [106 {107 id: 101,108 name: "Gaming",109 status: "Trending",110 creators: [111 { id: 1001, name: "PixelMaster", followers: 2800000, revenue: "$45.2K", rating: 4.8, contentCount: 328, avgViewTime: 45, status: "Partner" },112 { id: 1002, name: "RetroGamer93", followers: 1200000, revenue: "$28.5K", rating: 4.6, contentCount: 156, avgViewTime: 52, status: "Partner" },113 { id: 1003, name: "SpeedrunQueen", followers: 890000, revenue: "$22.1K", rating: 4.9, contentCount: 89, avgViewTime: 38, status: "Partner" },114 ],115 },116 {117 id: 102,118 name: "Music & Arts",119 status: "Growing",120 creators: [121 { id: 1101, name: "MelodyMaker", followers: 1650000, revenue: "$31.8K", rating: 4.7, contentCount: 203, avgViewTime: 28, status: "Partner" },122 { id: 1102, name: "DigitalArtist", followers: 720000, revenue: "$18.9K", rating: 4.5, contentCount: 127, avgViewTime: 35, status: "Affiliate" },123 { id: 1103, name: "JazzVibez", followers: 430000, revenue: "$12.4K", rating: 4.8, contentCount: 78, avgViewTime: 42, status: "Affiliate" },124 ],125 },126 {127 id: 103,128 name: "Cooking & Lifestyle",129 status: "Stable",130 creators: [131 { id: 1201, name: "ChefExtraordinaire", followers: 3200000, revenue: "$58.7K", rating: 4.9, contentCount: 245, avgViewTime: 22, status: "Partner" },132 { id: 1202, name: "HomeDecorGuru", followers: 980000, revenue: "$19.3K", rating: 4.4, contentCount: 134, avgViewTime: 18, status: "Affiliate" },133 ],134 },135 ],136 },137 {138 id: 2,139 name: "WatchNow",140 status: "Competitor",141 categories: [142 {143 id: 201,144 name: "Tech Reviews",145 status: "Hot",146 creators: [147 { id: 2001, name: "TechGuru2024", followers: 2100000, revenue: "$42.6K", rating: 4.6, contentCount: 189, avgViewTime: 35, status: "Partner" },148 { id: 2002, name: "GadgetWhisperer", followers: 1450000, revenue: "$29.1K", rating: 4.7, contentCount: 156, avgViewTime: 31, status: "Partner" },149 { id: 2003, name: "CodeReviewer", followers: 680000, revenue: "$16.8K", rating: 4.8, contentCount: 94, avgViewTime: 48, status: "Affiliate" },150 ],151 },152 {153 id: 202,154 name: "Fitness & Health",155 status: "Growing",156 creators: [157 { id: 2101, name: "FitnessPhenom", followers: 1890000, revenue: "$35.4K", rating: 4.5, contentCount: 312, avgViewTime: 25, status: "Partner" },158 { id: 2102, name: "YogaMaster", followers: 1100000, revenue: "$21.7K", rating: 4.9, contentCount: 178, avgViewTime: 33, status: "Partner" },159 ],160 },161 ],162 },163 {164 id: 3,165 name: "CreativeSpace",166 status: "Emerging",167 categories: [168 {169 id: 301,170 name: "Photography",171 status: "Niche",172 creators: [173 { id: 3001, name: "LensArtist", followers: 750000, revenue: "$18.2K", rating: 4.7, contentCount: 145, avgViewTime: 27, status: "Partner" },174 { id: 3002, name: "NatureShooter", followers: 520000, revenue: "$13.5K", rating: 4.6, contentCount: 98, avgViewTime: 29, status: "Affiliate" },175 { id: 3003, name: "PortraitPro", followers: 390000, revenue: "$9.8K", rating: 4.8, contentCount: 67, avgViewTime: 24, status: "Affiliate" },176 ],177 },178 {179 id: 302,180 name: "Animation & VFX",181 status: "Specialized",182 creators: [183 { id: 3101, name: "3DAnimator", followers: 640000, revenue: "$15.9K", rating: 4.9, contentCount: 58, avgViewTime: 41, status: "Partner" },184 { id: 3102, name: "VFXWizard", followers: 480000, revenue: "$12.3K", rating: 4.7, contentCount: 42, avgViewTime: 38, status: "Affiliate" },185 ],186 },187 ],188 },189 {190 id: 4,191 name: "EduStream",192 status: "Educational Focus",193 categories: [194 {195 id: 401,196 name: "Science & Math",197 status: "Educational",198 creators: [199 { id: 4001, name: "MathExplainer", followers: 1340000, revenue: "$26.8K", rating: 4.8, contentCount: 234, avgViewTime: 36, status: "Partner" },200 { id: 4002, name: "PhysicsPhun", followers: 890000, revenue: "$19.4K", rating: 4.6, contentCount: 167, avgViewTime: 42, status: "Partner" },201 { id: 4003, name: "ChemistryLab", followers: 560000, revenue: "$14.2K", rating: 4.7, contentCount: 89, avgViewTime: 33, status: "Affiliate" },202 ],203 },204 {205 id: 402,206 name: "History & Culture",207 status: "Informative",208 creators: [209 { id: 4101, name: "HistoryBuff", followers: 920000, revenue: "$18.6K", rating: 4.5, contentCount: 145, avgViewTime: 39, status: "Partner" },210 { id: 4102, name: "CultureExplorer", followers: 670000, revenue: "$15.1K", rating: 4.8, contentCount: 112, avgViewTime: 45, status: "Affiliate" },211 ],212 },213 ],214 },215];216217export const aggregateFunctionsConfig = {218 headers: aggregateFunctionsHeaders,219 rows: aggregateFunctionsData,220 tableProps: {221 rowGrouping: ["categories", "creators"] as string[],222 columnResizing: true,223 },224} as const;225
1<script lang="ts">2 import {SimpleTable} from "@simple-table/svelte"; import type { Theme } from "@simple-table/svelte";3 import { aggregateFunctionsConfig } from "./aggregate-functions.demo-data";4 import "@simple-table/svelte/styles.css";56 let { height = "400px", theme }: { height?: string | number; theme?: Theme } = $props();7</script>89<SimpleTable10 defaultHeaders={aggregateFunctionsConfig.headers}11 rows={aggregateFunctionsConfig.rows}12 rowGrouping={aggregateFunctionsConfig.tableProps.rowGrouping}13 columnResizing={true}14 {height}15 {theme}16/>
1import {SimpleTable} from "@simple-table/solid";import type { Theme } from "@simple-table/solid";2import { aggregateFunctionsConfig } from "./aggregate-functions.demo-data";3import "@simple-table/solid/styles.css";45export default function AggregateFunctionsDemo(props: {6 height?: string | number;7 theme?: Theme;8}) {9 return (10 <SimpleTable11 defaultHeaders={aggregateFunctionsConfig.headers}12 rows={aggregateFunctionsConfig.rows}13 rowGrouping={aggregateFunctionsConfig.tableProps.rowGrouping}14 columnResizing15 height={props.height ?? "400px"}16 theme={props.theme}17 />18 );19}
1import { SimpleTableVanilla } from "simple-table-core";2import type { Theme } from "simple-table-core";3import { aggregateFunctionsConfig } from "./aggregate-functions.demo-data";4import "simple-table-core/styles.css";56export function renderAggregateFunctionsDemo(7 container: HTMLElement,8 options?: { height?: string | number; theme?: Theme }9): SimpleTableVanilla {10 const table = new SimpleTableVanilla(container, {11 defaultHeaders: aggregateFunctionsConfig.headers,12 rows: aggregateFunctionsConfig.rows,13 rowGrouping: aggregateFunctionsConfig.tableProps.rowGrouping,14 columnResizing: true,15 height: options?.height ?? "400px",16 theme: options?.theme,17 });18 return table;19}202122// aggregate-functions.demo-data.ts23// Self-contained demo table setup for this example.24import type { HeaderObject } from "simple-table-core";252627export const aggregateFunctionsHeaders: HeaderObject[] = [28 { accessor: "name", label: "Name", width: 200, expandable: true, type: "string" },29 {30 accessor: "followers",31 label: "Followers",32 width: 120,33 type: "number",34 aggregation: { type: "sum" },35 valueFormatter: ({ value }) => {36 if (typeof value === "number") {37 return value >= 100000038 ? `${(value / 1000000).toFixed(1)}M`39 : value >= 100040 ? `${(value / 1000).toFixed(0)}K`41 : value.toString();42 }43 return "";44 },45 },46 {47 accessor: "revenue",48 label: "Monthly Revenue",49 width: 140,50 type: "string",51 aggregation: {52 type: "sum",53 parseValue: (value) => {54 if (typeof value !== "string") return 0;55 const numericValue = parseFloat(value.replace(/[$K]/g, ""));56 return isNaN(numericValue) ? 0 : numericValue;57 },58 },59 valueFormatter: ({ value }) => {60 if (typeof value === "number") return `$${value.toFixed(1)}K`;61 if (typeof value === "string") return value;62 return "";63 },64 },65 {66 accessor: "rating",67 label: "Rating",68 width: 100,69 type: "number",70 aggregation: { type: "average" },71 valueFormatter: ({ value }) => (typeof value === "number" ? `${value.toFixed(1)} ⭐` : ""),72 },73 {74 accessor: "contentCount",75 label: "Content",76 width: 90,77 type: "number",78 aggregation: { type: "sum" },79 },80 {81 accessor: "avgViewTime",82 label: "Avg Watch Time",83 width: 130,84 type: "number",85 aggregation: { type: "average" },86 valueFormatter: ({ value }) => (typeof value === "number" ? `${Math.round(value)}min` : ""),87 },88 { accessor: "status", label: "Status", width: 120, type: "string" },89];9091export const aggregateFunctionsData = [92 {93 id: 1,94 name: "StreamFlix",95 status: "Leading Platform",96 categories: [97 {98 id: 101,99 name: "Gaming",100 status: "Trending",101 creators: [102 { id: 1001, name: "PixelMaster", followers: 2800000, revenue: "$45.2K", rating: 4.8, contentCount: 328, avgViewTime: 45, status: "Partner" },103 { id: 1002, name: "RetroGamer93", followers: 1200000, revenue: "$28.5K", rating: 4.6, contentCount: 156, avgViewTime: 52, status: "Partner" },104 { id: 1003, name: "SpeedrunQueen", followers: 890000, revenue: "$22.1K", rating: 4.9, contentCount: 89, avgViewTime: 38, status: "Partner" },105 ],106 },107 {108 id: 102,109 name: "Music & Arts",110 status: "Growing",111 creators: [112 { id: 1101, name: "MelodyMaker", followers: 1650000, revenue: "$31.8K", rating: 4.7, contentCount: 203, avgViewTime: 28, status: "Partner" },113 { id: 1102, name: "DigitalArtist", followers: 720000, revenue: "$18.9K", rating: 4.5, contentCount: 127, avgViewTime: 35, status: "Affiliate" },114 { id: 1103, name: "JazzVibez", followers: 430000, revenue: "$12.4K", rating: 4.8, contentCount: 78, avgViewTime: 42, status: "Affiliate" },115 ],116 },117 {118 id: 103,119 name: "Cooking & Lifestyle",120 status: "Stable",121 creators: [122 { id: 1201, name: "ChefExtraordinaire", followers: 3200000, revenue: "$58.7K", rating: 4.9, contentCount: 245, avgViewTime: 22, status: "Partner" },123 { id: 1202, name: "HomeDecorGuru", followers: 980000, revenue: "$19.3K", rating: 4.4, contentCount: 134, avgViewTime: 18, status: "Affiliate" },124 ],125 },126 ],127 },128 {129 id: 2,130 name: "WatchNow",131 status: "Competitor",132 categories: [133 {134 id: 201,135 name: "Tech Reviews",136 status: "Hot",137 creators: [138 { id: 2001, name: "TechGuru2024", followers: 2100000, revenue: "$42.6K", rating: 4.6, contentCount: 189, avgViewTime: 35, status: "Partner" },139 { id: 2002, name: "GadgetWhisperer", followers: 1450000, revenue: "$29.1K", rating: 4.7, contentCount: 156, avgViewTime: 31, status: "Partner" },140 { id: 2003, name: "CodeReviewer", followers: 680000, revenue: "$16.8K", rating: 4.8, contentCount: 94, avgViewTime: 48, status: "Affiliate" },141 ],142 },143 {144 id: 202,145 name: "Fitness & Health",146 status: "Growing",147 creators: [148 { id: 2101, name: "FitnessPhenom", followers: 1890000, revenue: "$35.4K", rating: 4.5, contentCount: 312, avgViewTime: 25, status: "Partner" },149 { id: 2102, name: "YogaMaster", followers: 1100000, revenue: "$21.7K", rating: 4.9, contentCount: 178, avgViewTime: 33, status: "Partner" },150 ],151 },152 ],153 },154 {155 id: 3,156 name: "CreativeSpace",157 status: "Emerging",158 categories: [159 {160 id: 301,161 name: "Photography",162 status: "Niche",163 creators: [164 { id: 3001, name: "LensArtist", followers: 750000, revenue: "$18.2K", rating: 4.7, contentCount: 145, avgViewTime: 27, status: "Partner" },165 { id: 3002, name: "NatureShooter", followers: 520000, revenue: "$13.5K", rating: 4.6, contentCount: 98, avgViewTime: 29, status: "Affiliate" },166 { id: 3003, name: "PortraitPro", followers: 390000, revenue: "$9.8K", rating: 4.8, contentCount: 67, avgViewTime: 24, status: "Affiliate" },167 ],168 },169 {170 id: 302,171 name: "Animation & VFX",172 status: "Specialized",173 creators: [174 { id: 3101, name: "3DAnimator", followers: 640000, revenue: "$15.9K", rating: 4.9, contentCount: 58, avgViewTime: 41, status: "Partner" },175 { id: 3102, name: "VFXWizard", followers: 480000, revenue: "$12.3K", rating: 4.7, contentCount: 42, avgViewTime: 38, status: "Affiliate" },176 ],177 },178 ],179 },180 {181 id: 4,182 name: "EduStream",183 status: "Educational Focus",184 categories: [185 {186 id: 401,187 name: "Science & Math",188 status: "Educational",189 creators: [190 { id: 4001, name: "MathExplainer", followers: 1340000, revenue: "$26.8K", rating: 4.8, contentCount: 234, avgViewTime: 36, status: "Partner" },191 { id: 4002, name: "PhysicsPhun", followers: 890000, revenue: "$19.4K", rating: 4.6, contentCount: 167, avgViewTime: 42, status: "Partner" },192 { id: 4003, name: "ChemistryLab", followers: 560000, revenue: "$14.2K", rating: 4.7, contentCount: 89, avgViewTime: 33, status: "Affiliate" },193 ],194 },195 {196 id: 402,197 name: "History & Culture",198 status: "Informative",199 creators: [200 { id: 4101, name: "HistoryBuff", followers: 920000, revenue: "$18.6K", rating: 4.5, contentCount: 145, avgViewTime: 39, status: "Partner" },201 { id: 4102, name: "CultureExplorer", followers: 670000, revenue: "$15.1K", rating: 4.8, contentCount: 112, avgViewTime: 45, status: "Affiliate" },202 ],203 },204 ],205 },206];207208export const aggregateFunctionsConfig = {209 headers: aggregateFunctionsHeaders,210 rows: aggregateFunctionsData,211 tableProps: {212 rowGrouping: ["categories", "creators"] as string[],213 columnResizing: true,214 },215} as const;216
Basic Implementation
Aggregate functions are configured by adding the aggregation property to column headers. When data is grouped using row grouping, these functions automatically calculate summary values for each group level.
Aggregation Configuration
| Property | Required | Description | Example |
|---|---|---|---|
Property | Required | Description | Example |
HeaderObject.aggregation | Optional | Configuration object for aggregation functions that automatically calculate summary values for grouped data. Supports built-in types (sum, average, count, min, max) and custom functions. Options: sum average count min max custom |
Aggregation Types
Sum Aggregation
Calculates the total of all numeric values in a group.
Perfect for totaling budgets, employee counts, or any cumulative metrics.
Average Aggregation
Computes the arithmetic mean of all values in a group.
Ideal for ratings, performance scores, or any metric where the mean is meaningful.
Count Aggregation
Counts the number of non-null values in a group.
Useful for counting projects, tasks, or any discrete items within groups.
Min/Max Aggregation
Finds the minimum or maximum value in a group.
Great for finding ranges, extremes, or boundary values in your data.
Advanced: Custom Aggregation & Value Parsing
For complex scenarios, you can define custom aggregation functions and parse string values before aggregation.
💡 Pro Tips
- Combine aggregations with custom cell renderers to format results appropriately
- Use parseValue when your source data contains formatted strings like currencies
- Aggregations work at every level of row grouping, providing hierarchical summaries
- Custom aggregation functions receive all values in the group as an array
Related Features
- Row Grouping - Learn about organizing hierarchical data
- Nested Tables - Display different columns at each hierarchy level
- Value Formatter - Format aggregated values for display