Recursive List Flattening
AdvancedFlatten arbitrarily nested lists into a single flat list using recursion with the @ self-reference operator.
The Problem
JSON APIs and nested data sources often return deeply nested structures. {1, {2, 3}, {4, {5, 6}}} needs to become {1, 2, 3, 4, 5, 6}. M has no built-in List.Flatten, so you build it with recursion.
The Recursive Flatten Function
let
Flatten = (lst as list) as list =>
List.Combine(
List.Transform(lst, each
if _ is list then @Flatten(_) else {_}
)
)
in
Flatten({1, {2, 3}, {4, {5, 6}}})
// Result: {1, 2, 3, 4, 5, 6}The @ prefix is M's self-reference operator — it lets a function call itself by name before that name is fully bound. Without @, the recursive call would fail with an error.
How It Works
List.Transformmaps over every item in the list- If an item is a list, recursively call
@Flattenon it - If an item is not a list, wrap it in a single-item list
{_} List.Combinemerges all the resulting lists into one flat list
Flatten with a Depth Limit
If you only want to flatten one or two levels deep:
let
FlattenN = (lst as list, depth as number) as list =>
if depth = 0 then lst
else List.Combine(
List.Transform(lst, each
if _ is list then @FlattenN(_, depth - 1) else {_}
)
)
in
FlattenN({1, {2, {3, 4}}, 5}, 1)
// Result: {1, 2, {3, 4}, 5} — only one level flattenedFlattening Nested Records into a Table
The same recursive pattern works for nested records from JSON:
let
FlattenRecord = (rec as record, prefix as text) as list =>
List.Combine(
List.Transform(Record.FieldNames(rec), each
let value = Record.Field(rec, _)
in if value is record
then @FlattenRecord(value, prefix & _ & ".")
else {[Key = prefix & _, Value = value]}
)
),
Source = Json.Document(Web.Contents("https://api.example.com/data")),
Flattened = FlattenRecord(Source, ""),
Result = Table.FromRecords(Flattened)
in
ResultThis turns a deeply nested JSON object into a two-column table of Key / Value pairs, where keys use dot notation like "address.city".
Important Caveats
- Stack depth: M does not optimize tail calls. Very deeply nested lists (hundreds of levels) can cause stack overflow errors. For known shallow nesting, prefer iterative approaches with
List.Accumulate. - Performance: Recursive functions re-evaluate on each call. Buffer intermediate results if the function is called repeatedly on large lists.
- Type checking:
_ is listchecks the runtime type. This works for lists of mixed types, including lists containing nulls —nullis not a list and will be treated as a leaf value.