List.Accumulate()

Learn how List.Accumulate reduces a list to a single value by carrying state across every item — the general-purpose function behind running totals and custom aggregations that no built-in function covers.

List.Accumulate()

List.Accumulate() walks through a list one value at a time, carrying a running "state" forward from each step to the next — M's general-purpose reduce function, for building a single result out of a list when no built-in aggregation (List.Sum, List.Max, and so on) already does it.

List.Accumulate(
    list as list,
    seed as any,
    accumulator as function
) as any
  • seed — the starting value, before any list items have been processed.
  • accumulator — a two-argument function: (state, current) => newState, called once per list item.

Basic Example: Reimplementing List.Sum

List.Accumulate({1, 2, 3, 4}, 0, (state, current) => state + current)
seed = 0
state=0, current=1 -> newState=1
state=1, current=2 -> newState=3
state=3, current=3 -> newState=6
state=6, current=4 -> newState=10

Result: 10

List.Sum({1,2,3,4}) already does exactly this — List.Accumulate() is worth reaching for once the logic needed is more than a single built-in function already covers.


A Real Use Case: String Concatenation With Custom Formatting

List.Accumulate(
    {"Alice", "Bob", "Carol"}, "",
    (state, current) => if state = "" then current else state & ", " & current
)
Result: "Alice, Bob, Carol"

Text.Combine({"Alice","Bob","Carol"}, ", ") already does this simpler case too — List.Accumulate() earns its place once the joining logic needs to be conditional (skip blanks, format differently for the first item, stop early under some condition), not just a fixed separator.


Building Up a Table or Record

The state carried between steps doesn't have to be a number or text — it can be a table, a record, or any other structure, which is where List.Accumulate() handles cases nothing else does directly.

List.Accumulate(
    {1, 2, 3},
    #table({"Step", "Value"}, {}),
    (state, current) => Table.InsertRows(state, Table.RowCount(state), {[Step = current, Value = current * current]})
)
Step | Value
1    | 1
2    | 4
3    | 9

Each pass appends one more row to the table being carried forward as state.


List.Accumulate vs. List.Generate

Both build something up across repeated steps, but for different purposes:

List.AccumulateList.Generate
InputAn existing list, one pass per itemNo input list — generates values from a condition
OutputA single final valueA list of every intermediate value produced
Typical useReducing a known list to one resultProducing a new sequence that doesn't exist yet

See List.Generate() for the sequence-building side of this pair.


Common Mistakes

Getting the Accumulator's Argument Order Backwards

(current, state) => state + current

The first argument is always the running state, the second is the current list item — swapping them silently produces wrong results rather than an error, since both are usually the same type and the expression can still evaluate without complaint.

Reaching for List.Accumulate When a Built-In Already Exists

Rebuilding List.Sum, List.Max, or List.Count from scratch with List.Accumulate() works, but is harder to read and slower to write than the built-in — worth checking the M Function Reference first for anything that looks like a standard aggregation.

Forgetting the Seed's Type Has to Match What the Accumulator Eventually Returns

List.Accumulate({1, 2, 3}, 0, (state, current) => state & Text.From(current))

Starting with a numeric seed (0) while the accumulator concatenates text produces a type error on the first iteration, since 0 & "1" mixes a number and text in a context expecting one consistent type. The seed needs to match the type the accumulator function actually produces — here, an empty string "" rather than 0.

Not Handling an Empty Input List

List.Accumulate({}, seed, accumulator) simply returns seed unchanged, without ever calling the accumulator — usually the right behavior, but worth confirming explicitly if the seed value isn't a sensible "nothing to accumulate" result on its own.


Next Steps