← All articles
DataWeave · MuleSoft · Quick Reads

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

RequirementPrefer
Transform each itemmap
Keep selected itemsfilter
Group items by a keygroupBy
Simple numeric totalsum when appropriate
Build one accumulated resultreduce
Build lookup object from arrayreduce can be useful
Need predictable empty-input resultexplicit 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.

CONTINUE READING

Explore closely related architecture, integration and implementation topics.