---
source: https://carbone.io/documentation/design/computation/aggregation.html
title: "How to perform calculations on array data?"
description: "How to perform calculations on array data?"
generated_at: "2026-07-13"
---

# Aggregators

How to perform calculations on array data?  
ENTERPRISE FEATURE Available for:  
✓ Carbone Cloud  
✓ Carbone On-premise  
✗ Embedded Carbone JS   v4.0+ 

## Overview

An aggregate formatter processes a set of values and returns a single aggregated result.  
It can be used as a [standalone expression](/documentation/design/computation/aggregation.md#learn-with-aggsum-partitionby) or within [loops](/documentation/design/computation/aggregation.md#learn-with-aggsum-partitionby-aggregate-and-loop) to calculate [custom groupings](/documentation/design/computation/aggregation.md#learn-with-aggsum-partitionby-aggregate-for-independent-groups), such as [sub-totals](/documentation/design/computation/aggregation.md#learn-with-aggsum-partitionby-aggregator-with-nested-arrays) or [cumulative totals](/documentation/design/computation/aggregation.md#learn-with-aggsum-partitionby-aggregate-and-loop).

The following aggregators are available:

-   [aggSum](#learn-with-aggsum-partitionby): Calculates the sum of all values in a dataset.
-   [aggAvg](#aggavg-partitionby): Computes the average of values in a dataset.
-   [aggMin](#aggmin-partitionby): Finds the minimum value in a dataset.
-   [aggMax](#aggmax-partitionby): Finds the maximum value in a dataset.
-   [aggCount](#aggcount-partitionby): Counts the total number of items in a dataset.
-   [aggCountD](#aggcountd-partitionby): Counts the number of distinct items in a dataset.
-   [aggStr](#aggstr-separator-partitionby): Concatenates all strings in a dataset, using a specified separator.
-   [aggStrD](#aggstrd-separator-partitionby): Concatenates distinct strings in a dataset, using a specified separator.
-   [cumSum](#cumsum-partitionby): Calculates cumulative sums (running totals) as the dataset progresses through a series.
-   [cumCount](#cumcount-partitionby): Assigns a sequential integer to each row in a list.
-   [cumCountD](#cumcountd-partitionby): Assigns a sequential integer to each distinct item in a list.

> ⚠️ **Warning:** Aggregators have a limitation when accessing a value within a sub-object, such as `{d[].sub.qty}`.  
> To handle this issue, use the `:print` formatter to aggregate values coming from sub-objects. For example: `{d[]:print(.sub.qty):aggSum}`

## Learn with :aggSum(partitionBy)

Here is an example using `:aggSum`, which calculates and returns the total sum of all values in a dataset.

Carbone automatically iterates through the array and calculates the sum of all its elements if the `[]` brackets are left empty. [Array filters](/documentation/design/repetitions/filtering.md) can be used to reduce the dataset by applying specific conditions.

```cdata
{
  "cars": [
    { "brand":"Lexus"   , "qty":1 , "sort":1 },
    { "brand":"Faraday" , "qty":4 , "sort":4 },
    { "brand":"Venturi" , "qty":3 , "sort":3 },
    { "brand":"Faraday" , "qty":2 , "sort":2 },
    { "brand":"Aptera"  , "qty":1 , "sort":1 },
    { "brand":"Venturi" , "qty":10, "sort":5 }
  ]
}
```

```ctemplate
Total:
{d.cars[].qty:aggSum}
Total with filters:
{d.cars[sort>1].qty:aggSum}
Total with multiplication:
{d.cars[sort>1].qty:mul(.sort):aggSum:formatC}
```

```cresult
Total:
21
Total with filters:
19
Total with multiplication:
79.00 €
```

### Aggregate and Loop

Perform aggregation and looping simultaneously.

You can calculate and display the cumulative sum of values using `:cumSum` instead of `:aggSum`. This allows you to track the running total as you iterate through the data.

```cdata
[
  { "brand":"Lexus"   , "qty":1  },
  { "brand":"Faraday" , "qty":4  },
  { "brand":"Venturi" , "qty":3  },
  { "brand":"Faraday" , "qty":2  },
  { "brand":"Aptera"  , "qty":1  },
  { "brand":"Venturi" , "qty":10 }
]
```

**Template:**

| Brand | Total | Cumulative |
| --- | --- | --- |
| {d[i].brand} | {d[i].qty:aggSum} | {d[i].qty:cumSum} |
| {d[i+1].brand} |   |   |

**Result:**

| Brand | Total | Cumulative |
| --- | --- | --- |
| Lexus | 21 | 1 |
| Faraday | 21 | 5 |
| Venturi | 21 | 8 |
| Faraday | 21 | 10 |
| Aptera | 21 | 11 |
| Venturi | 21 | 21 |

### Aggregate for independent groups

Carbone aggregators support one or more parameters that allow you to divide data into separate groups (or "partitions") for independent calculations. This functionality is similar to the `PARTITION BY` clause in SQL.

By specifying grouping parameters, you can ensure that the aggregation is performed independently for each defined group, making it easier to generate structured reports with grouped totals, averages, or other metrics.

```cdata
[
  { "brand":"Lexus"   , "qty":1  },
  { "brand":"Faraday" , "qty":4  },
  { "brand":"Venturi" , "qty":3  },
  { "brand":"Faraday" , "qty":2  },
  { "brand":"Aptera"  , "qty":1  },
  { "brand":"Venturi" , "qty":10 }
]
```

**Template:**

| Brand | Total by brand |
| --- | --- |
| {d[i].brand} | {d[i].qty:aggSum(.brand)} |
| {d[i+1].brand} |   |

**Result:**

| Brand | Total by brand |
| --- | --- |
| Lexus | 1 |
| Faraday | 6 |
| Venturi | 13 |
| Faraday | 6 |
| Aptera | 1 |
| Venturi | 13 |

### Aggregator with nested arrays

Calculating subtotals for nested arrays.

```cdata
[
  { 
    "country" : "France",
    "cities" : [
      { "id" : "Paris"     , "cars" : 100 },
      { "id" : "Nantes"    , "cars" : 50  },
      { "id" : "Pouzauges" , "cars" : 1   }
    ]
  },
  { 
    "country" : "Italy",
    "cities" : [
      { "id" : "Rome"   , "cars" : 20 },
      { "id" : "Venise" , "cars" : 2 }
    ]
  }
]
```

**Template:**

| Brand | Total per city |
| --- | --- |
| {d[i].country} | {d[i].cities[].cars:aggSum} |
| {d[i+1].country} |   |

**Result:**

| Brand | Total per city |
| --- | --- |
| France | 151 |
| Italy | 22 |

Get inspired by one of our real-life examples: [Invoice](/examples/invoice-simple/index.md), [Bank Statement](/examples/bank-statement-expert/index.md), [Store Inventory](/examples/shoes/index.md) or [Planning with subtotals](/examples/planification-medium/index.md)

## :aggAvg(partitionBy)

The `:aggAvg` aggregator calculates and returns the average of a dataset.

For more details on using aggregators and partitioning, refer to the [examples](/documentation/design/computation/aggregation.md#learn-with-aggsum-partitionby) provided for `:aggSum`.

```cdata
{
  "cars": [
    { "brand":"Lexus"   , "qty":1 , "sort":1 },
    { "brand":"Faraday" , "qty":4 , "sort":4 },
    { "brand":"Venturi" , "qty":3 , "sort":3 },
    { "brand":"Faraday" , "qty":2 , "sort":2 },
    { "brand":"Aptera"  , "qty":1 , "sort":1 },
    { "brand":"Venturi" , "qty":10, "sort":5 }
  ]
}
```

```ctemplate
Average:
{d.cars[].qty:aggAvg}
Average with filters:
{d.cars[sort>1].qty:aggAvg}
Average with multiplication:
{d.cars[sort>1].qty:mul(.sort):aggAvg:formatC}
```

```cresult
Average:
3.5
Average with filters:
4.75
Average with multiplication:
19.75 €
```

Get inspired by one of our real-life examples: [Sensor Readings](/examples/sensor-readings/index.md)

## :aggMin(partitionBy)

The `:aggMin` aggregator returns the minimum value of a dataset.

For more details on using aggregators and partitioning, refer to the [examples](/documentation/design/computation/aggregation.md#learn-with-aggsum-partitionby) provided for `:aggSum`.

```cdata
{
  "cars": [
    { "brand":"Lexus"   , "qty":1 , "sort":1 },
    { "brand":"Faraday" , "qty":4 , "sort":4 },
    { "brand":"Venturi" , "qty":3 , "sort":3 },
    { "brand":"Faraday" , "qty":2 , "sort":2 },
    { "brand":"Aptera"  , "qty":1 , "sort":1 },
    { "brand":"Venturi" , "qty":10, "sort":5 }
  ]
}
```

```ctemplate
Minimum:
{d.cars[].qty:aggMin}
Minimum with filters:
{d.cars[sort>1].qty:aggMin}
```

```cresult
Minimum:
1
Minimum with filters:
2
```

## :aggMax(partitionBy)

The `:aggMax` aggregator returns the maximum value of a dataset.

For more details on using aggregators and partitioning, refer to the [examples](/documentation/design/computation/aggregation.md#learn-with-aggsum-partitionby) provided for `:aggSum`.

```cdata
{
  "cars": [
    { "brand":"Lexus"   , "qty":1 , "sort":1 },
    { "brand":"Faraday" , "qty":4 , "sort":4 },
    { "brand":"Venturi" , "qty":3 , "sort":3 },
    { "brand":"Faraday" , "qty":2 , "sort":2 },
    { "brand":"Aptera"  , "qty":1 , "sort":1 },
    { "brand":"Venturi" , "qty":10, "sort":5 }
  ]
}
```

```ctemplate
Maximum:
{d.cars[].qty:aggMax}
Maximum with filters:
{d.cars[sort>1].qty:aggMax}
```

```cresult
Maximum:
10
Maximum with filters:
10
```

## :aggCount(partitionBy)

The `:aggCount` aggregator returns the number of items of a dataset.

For more details on using aggregators and partitioning, refer to the [examples](/documentation/design/computation/aggregation.md#learn-with-aggsum-partitionby) provided for `:aggSum`.

> ℹ️ **Note:** It counts items regardless of the value passed on the left side of the formatter (e.g., `{d[i].qty}` in the example). The value is ignored by the formatter.

```cdata
{
  "cars": [
    { "brand":"Lexus"   , "qty":1 , "sort":1 },
    { "brand":"Faraday" , "qty":4 , "sort":4 },
    { "brand":"Venturi" , "qty":3 , "sort":3 },
    { "brand":"Faraday" , "qty":2 , "sort":2 },
    { "brand":"Aptera"  , "qty":1 , "sort":1 },
    { "brand":"Venturi" , "qty":10, "sort":5 }
  ]
}
```

```ctemplate
Number of items:
{d.cars[].qty:aggCount}
Number of items with filters:
{d.cars[sort>1].qty:aggCount}
```

```cresult
Number of items:
6
Number of items with filters:
4
```

## :aggCountD(partitionBy)

The `:aggCountD` aggregator calculates and returns the number of **Distinct** items in a dataset.

For more details on using aggregators and partitioning, refer to the [examples](/documentation/design/computation/aggregation.md#learn-with-aggsum-partitionby) provided for `:aggSum`.

```cdata
{
  "cars": [
    { "brand":"Lexus"   , "qty":1 , "sort":1 },
    { "brand":"Faraday" , "qty":4 , "sort":4 },
    { "brand":"Venturi" , "qty":3 , "sort":3 },
    { "brand":"Faraday" , "qty":2 , "sort":2 },
    { "brand":"Aptera"  , "qty":1 , "sort":1 },
    { "brand":"Venturi" , "qty":10, "sort":5 }
  ]
}
```

```ctemplate
Number of brands:
{d.cars[].brand:aggCountD}
```

```cresult
Number of brands:
4
```

## :aggStr(separator, partitionBy)

The `:aggStr` aggregator concatenates all values in a dataset, separated by `", "` (the default `separator`).

For more details on using aggregators and partitioning, refer to the [examples](/documentation/design/computation/aggregation.md#learn-with-aggsum-partitionby) provided for `:aggSum`.

```cdata
{
  "cars": [
    { "brand":"Lexus"   , "qty":1 , "sort":1 },
    { "brand":"Faraday" , "qty":4 , "sort":4 },
    { "brand":"Venturi" , "qty":3 , "sort":3 },
    { "brand":"Faraday" , "qty":2 , "sort":2 },
    { "brand":"Aptera"  , "qty":1 , "sort":1 },
    { "brand":"Venturi" , "qty":10, "sort":5 }
  ]
}
```

```ctemplate
Cars list:
{d.cars[].brand:aggStr}
{d.cars[].brand:aggStr(' | ')}
```

```cresult
Cars list:
Lexus, Faraday, Venturi, Faraday, Aptera, Venturi
Lexus | Faraday | Venturi | Faraday | Aptera | Venturi
```

## :aggStrD(separator, partitionBy)

The `:aggStrD` aggregator concatenates all **Distinct** values in a dataset, separated by `", "` (the default `separator`).

For more details on using aggregators and partitioning, refer to the [examples](/documentation/design/computation/aggregation.md#learn-with-aggsum-partitionby) provided for `:aggSum`.

```cdata
{
  "cars": [
    { "brand":"Lexus"   , "qty":1 , "sort":1 },
    { "brand":"Faraday" , "qty":4 , "sort":4 },
    { "brand":"Venturi" , "qty":3 , "sort":3 },
    { "brand":"Faraday" , "qty":2 , "sort":2 },
    { "brand":"Aptera"  , "qty":1 , "sort":1 },
    { "brand":"Venturi" , "qty":10, "sort":5 }
  ]
}
```

```ctemplate
Cars list:
{d.cars[].brand:aggStrD}
{d.cars[].brand:aggStrD(' | ')}
```

```cresult
Cars list:
Lexus, Faraday, Aptera, Venturi
Lexus | Faraday | Aptera | Venturi
```

## :cumSum(partitionBy)

The `:cumSum` formatter, also called a running total, calculates the cumulative sum of data by continuously adding values as it moves through the series.

```cdata
[
  { "brand":"Lexus"   , "qty":1  },
  { "brand":"Faraday" , "qty":4  },
  { "brand":"Venturi" , "qty":3  },
  { "brand":"Faraday" , "qty":2  },
  { "brand":"Aptera"  , "qty":1  },
  { "brand":"Venturi" , "qty":10 }
]
```

**Template:**

| Brand | Running total |
| --- | --- |
| {d[i].brand} | {d[i].qty:cumSum} |
| {d[i+1].brand} |   |

**Result:**

| Brand | Running total |
| --- | --- |
| Lexus | 1 |
| Faraday | 5 |
| Venturi | 8 |
| Faraday | 10 |
| Aptera | 11 |
| Venturi | 21 |

Get inspired by one of our real-life examples: [Bank Statement](/examples/bank-statement-expert/index.md), or [Planning with subtotals](/examples/planification-medium/index.md)

## :cumCount(partitionBy)

The `:cumCount` formatter returns a sequential integer to each row in a list.

A dynamic variable, `partition`, can be passed to `:cumCount(partition)`. This ensures the row numbering resets to 1 for each partition and increments sequentially within that partition.

For more details on using aggregators and partitioning, refer to the [examples](/documentation/design/computation/aggregation.md#learn-with-aggsum-partitionby) provided for `:aggSum`.

> ℹ️ **Note:** It counts items regardless of the value passed on the left side of the formatter (e.g., `{d[i].qty}` in the example). The value is ignored by the formatter.

```cdata
[
  { "brand":"Lexus"   , "qty":1  },
  { "brand":"Faraday" , "qty":4  },
  { "brand":"Faraday" , "qty":2  },
  { "brand":"Faraday" , "qty":2  },
  { "brand":"Venturi" , "qty":3  },
  { "brand":"Venturi" , "qty":3  },
  { "brand":"Aptera"  , "qty":1  },
  { "brand":"Venturi" , "qty":10 }
]
```

**Template:**

| Brand | Count | Count by brand |
| --- | --- | --- |
| {d[i].brand} | {d[i].qty:cumCount} | {d[i].qty:cumCount(.brand)} |
| {d[i+1].brand} |   |   |

**Result:**

| Brand | Count | Count by brand |
| --- | --- | --- |
| Lexus | 1 | 1 |
| Faraday | 2 | 1 |
| Faraday | 3 | 2 |
| Faraday | 4 | 3 |
| Venturi | 5 | 1 |
| Venturi | 6 | 2 |
| Aptera | 7 | 1 |
| Venturi | 8 | 3 |

Get inspired by one of our real-life examples: [Bank Statement](/examples/bank-statement-simple/index.md) or [Gallery of images](/examples/gallery-simple/index.md)

## :cumCountD(partitionBy)

The `:cumCountD` counts the number of **Distinct** items in a list.

For more details on using aggregators and partitioning, refer to the [examples](/documentation/design/computation/aggregation.md#learn-with-aggsum-partitionby) provided for `:aggSum`.

```cdata
[
  { "brand":"Lexus"   , "qty":1  },
  { "brand":"Faraday" , "qty":4  },
  { "brand":"Faraday" , "qty":2  },
  { "brand":"Venturi" , "qty":3  },
  { "brand":"Aptera"  , "qty":1  },
  { "brand":"Venturi" , "qty":10 }
]
```

**Template:**

| Brand | Count |
| --- | --- |
| {d[i].brand} | {d[i].brand:cumCountD} |
| {d[i+1].brand} |   |

**Result:**

| Brand | Count |
| --- | --- |
| Lexus | 1 |
| Faraday | 2 |
| Faraday | 2 |
| Venturi | 3 |
| Aptera | 4 |
| Venturi | 4 |

## :aggSum:cumSum

The `:aggSum` and `:cumSum` formatters can be used together in specific cases, such as calculating the total number of cars per country while also displaying a running total.

```cdata
[
  { 
    "country" : "France",
    "cities" : [
      { "id" : "Paris"     , "cars" : 100 },
      { "id" : "Nantes"    , "cars" : 50  },
      { "id" : "Pouzauges" , "cars" : 1   }
    ]
  },
  { 
    "country" : "Italy",
    "cities" : [
      { "id" : "Rome"   , "cars" : 20 },
      { "id" : "Venise" , "cars" : 2 }
    ]
  }
]
```

**Template:**

| Brand | Running total |
| --- | --- |
| {d[i].country} | {d[i].cities[].cars:aggSum:cumSum} |
| {d[i+1].country} |   |

**Result:**

| Brand | Running total |
| --- | --- |
| France | 151 |
| Italy | 173 |

## Related topics

- [Store and transform](/documentation/design/computation/store-and-transform.md)
- [Simple Mathematics](/documentation/design/computation/simple-mathematics.md)
