Practical DataWeave reduce Patterns
reduce is powerful because it can turn an array into almost any accumulated result: a number, string, object, or another array.
It is also easy to make harder to understand than necessary.
The basic model is:
current item + accumulated result → new accumulated result
DataWeave repeats that operation for the array elements in order.
Start with a Sum
Input:
[10, 20, 30]
Transformation:
%dw 2.0
output application/json
---
payload reduce ((item, total = 0) -> total + item)
Output:
60
The accumulator begins at 0.
Conceptually:
0 + 10 = 10
10 + 20 = 30
30 + 30 = 60
The Initial Accumulator Matters
The initial accumulator communicates what type of result you are building.
acc = 0
usually means a numeric result.
acc = ""
means a string result.
acc = []
means an array result.
acc = {}
means an object result.
Choosing an explicit initial value also gives predictable behavior for an empty input array.
For example:
[] reduce ((item, total = 0) -> total + item)
returns 0, while a reduction without an initial accumulator can return null for an empty array.
Sum a Field from Business Records
Input:
[
{ "orderId": "O1", "amount": 100 },
{ "orderId": "O2", "amount": 250 },
{ "orderId": "O3", "amount": 50 }
]
Transformation:
payload reduce ((order, total = 0) ->
total + (order.amount default 0)
)
Result:
400
Whether a missing amount should be treated as zero is a business decision. If a missing amount represents invalid data, validation may be more appropriate than silently defaulting it.
Build an Object Index
Suppose you frequently need to find a record by its ID.
Input:
[
{ "id": "A1", "name": "Acme" },
{ "id": "A2", "name": "Global Media" }
]
You can reduce the array into an object keyed by ID:
%dw 2.0
output application/json
---
payload reduce ((item, index = {}) ->
index ++ {
(item.id): item
}
)
Output:
{
"A1": { "id": "A1", "name": "Acme" },
"A2": { "id": "A2", "name": "Global Media" }
}
This changes the access pattern from repeatedly searching an array to reading a known key from an object.
Be explicit about what should happen when duplicate IDs appear. With object concatenation, a later value for the same key can replace the earlier value.
Build a Summary Object
reduce can calculate multiple values in one accumulator.
%dw 2.0
output application/json
---
payload reduce ((order, summary = {
count: 0,
total: 0
}) -> {
count: summary.count + 1,
total: summary.total + (order.amount default 0)
})
For three orders totaling 400, the result is:
{
"count": 3,
"total": 400
}
This pattern is useful when the output is a true aggregation rather than a transformed copy of each input item.
Use map When You Still Want One Output per Input
If your requirement is:
one input record → one transformed output record
use map.
For example:
payload map (account) -> {
id: account.Id,
name: account.Name
}
Using reduce to build the same array manually would work, but it would be less direct.
A useful distinction is:
map → transform each element
filter → choose elements
reduce → combine elements into accumulated state
Use Built-In Aggregations When They Express the Intent Better
Do not use reduce merely because it is flexible.
For a straightforward numeric total, this can be clearer:
sum(payload.amount)
Likewise, functions such as groupBy, distinctBy, map, and filter often express common operations more clearly than a custom reduction.
Use reduce when the accumulator itself is the important part of the transformation.
Build an Array Selectively
You can use an array accumulator when the output depends on accumulated conditional logic:
payload reduce ((account, result = []) ->
if (account.active default false)
result ++ [{
id: account.id,
name: account.name
}]
else
result
)
But if the only requirement is "keep active accounts and transform them," this is clearer:
payload
filter (account) -> account.active default false
map (account) -> {
id: account.id,
name: account.name
}
Prefer the expression that makes the business intention easiest to read.
A More Useful Integration Pattern: Index Once, Lookup Many Times
Suppose you have orders and a reference list of regions:
{
"regions": [
{ "code": "US-E", "name": "US East" },
{ "code": "US-W", "name": "US West" }
]
}
Instead of filtering the reference array repeatedly for every business record, first build an index:
var regionByCode =
payload.regions reduce ((region, index = {}) ->
index ++ {(region.code): region}
)
Then a transformation can access:
regionByCode[order.regionCode]
This can also make the transformation easier to understand because the lookup structure has a clear name.
For very large reference datasets, memory and alternative lookup strategies still need consideration.
Keep the Reducer Small
If the reduction body becomes a page of nested conditions, move business logic into named functions or reconsider whether reduce is the right abstraction.
For example:
fun addToSummary(order, summary) = {
count: summary.count + 1,
total: summary.total + (order.amount default 0)
}
---
payload reduce ((order, summary = {count: 0, total: 0}) ->
addToSummary(order, summary)
)
The main expression now communicates the operation without hiding the accumulator behavior.
Quick Decision Guide
| Requirement | Prefer |
|---|---|
| Transform each item | map |
| Keep selected items | filter |
| Group items by a key | groupBy |
| Simple numeric total | sum when appropriate |
| Build one accumulated result | reduce |
| Build lookup object from array | reduce can be useful |
| Need predictable empty-input result | explicit initial accumulator |
Practical Rule
Before writing reduce, finish this sentence:
I am combining all of these input elements into one ______.
If the blank is naturally total, summary, index, string, or another accumulated structure, reduce is probably a good fit.
If the answer is simply "array with one transformed item for every input item," reach for map instead.