Map arrays with iteration scopes
Order lines, invoice items, tags — transactional data lives in arrays, and mapping them is more than connecting two paths. Should each source element become a target element? Should a list collapse into one value (a total, a count, a joined string)? Should a single value be wrapped into an array? Cardinality-aware array mapping answers this with one concept: the iteration scope — a rule that pairs a source array with a target, owns the per-element attribute mappings inside it, and carries an explicit strategy for how elements correspond.
All three array scenarios ride on the same scope:
| Scenario | Strategy | Badge |
|---|---|---|
| Array → array (element-wise) | positional, keyed, or filtered | N→N |
| Array → single value | collapse + a collapse mode | N→1 |
| Single value → array | construct | 1→N |
By the end of this page you will have:
- Enabled the array-mapping canvas surface.
- Created an iteration scope by mapping
[*]paths — manually and via Auto-map. - Read the scope chip: strategy, element-attribute count, and status.
- Collapsed an array into a single target value and overridden the collapse mode.
- Confirmed your overrides survive an Auto-map re-run.
- A MapCraft project open at
/mapcraft/{projectId}with source and target schemas attached. - At least one array on each side — e.g. source
order.lines[*]and a targetinvoice.items[*], plus a scalar target likeinvoice.totalfor the collapse scenario. NEXT_PUBLIC_MAPCRAFT_ARRAY_MAPPING=trueset on the frontend deployment (see Step 1).
Step 1 — Enable the canvas surface
The scope chips ship behind a progressive-rollout flag:
NEXT_PUBLIC_MAPCRAFT_ARRAY_MAPPING=true # also accepts 1 / yes / on
NEXT_PUBLIC_* values are inlined into the client bundle at build time —
flipping the flag means redeploying the frontend.
The flag gates only the visual chips. The backend always groups array
([*]) edges into iteration scopes, so mappings you create while the flag
is off are already scope-correct when the chips appear.
Step 2 — Create a scope by drawing an array edge
Drag a connection from an array-nested source field to an array-nested target field — for example:
order.lines[*].sku → invoice.items[*].sku
Because both paths contain [*], MapCraft doesn't create a bare scalar
edge. It folds the connection into an iteration scope keyed by the
target array container (invoice.items), and registers sku → sku as an
element-attribute rule inside it.
Draw a second array edge to the same target container —
order.lines[*].qty → invoice.items[*].quantity — and it attaches to the
same scope. One scope per target array; N attribute rules inside it.
On the canvas, the scope renders as a chip with an [] badge:
order.lines → invoice.items, an N→N marker, and an attribute count
(2 attrs).
Step 3 — Or let Auto-map pair the arrays
Click Auto-map. After its scalar tiers, Auto-map pairs source and target array containers, recurses into the element attributes, and infers how elements correspond:
keyed— elements match by an identifying attribute (id/sku/code-like fields are detected as keys).positional— elements match by index.
The resulting scopes arrive with origin = "ai" and render the same chips
as Step 2. Hover a chip to read the scope's natural-language description.
Step 4 — Pick the right strategy
Every chip has an inline strategy picker:
| Strategy | Use when |
|---|---|
positional | i-th source element maps to i-th target element. |
keyed | Elements pair by a key attribute regardless of order. |
filtered | Only elements passing a condition map across. |
construct | A single source value is wrapped into a target array. |
collapse | The whole array reduces to one target value (Step 5). |
Changing the strategy saves immediately and stamps the scope origin = "manual" — the chip gains an edited badge (hover text: "Edited by you
— preserved across Auto-Map re-runs").
Step 5 — Collapse an array into one value
For an array → scalar target (order.lines[*].amount → invoice.total):
- Map the array-nested source to the scalar target. The scope is created
with strategy
collapseand the badge flips toN→1. - When Auto-map authored the scope, it pre-applies the recommended
collapse mode (numeric amounts →
sum). You can override it. - With
collapseselected, a second dropdown appears — the collapse mode:
| Mode | Result |
|---|---|
first / last | One element's value. |
sum / avg / min / max | Numeric reduction. |
count | Element count. |
concat | Values joined into one string. |
distinct | De-duplicated values. |
filter | Values passing a condition. |
Scope rules compile per export target: Python list comprehensions, JSONata
$map/built-ins, and SQL LATERAL jsonb_array_elements — including nested
arrays. What you preview in MapCraft is the same logic FlowBridge runs and
the same SQL your warehouse executes.
Step 6 — Re-run Auto-map safely
Iteration scopes follow the same origin rules as
combined N→1 rules: re-running Auto-map
regenerates only origin = "ai" scopes. Any scope you created manually or
overrode (strategy or collapse mode) is origin = "manual" and is left
untouched — the edited badge tells you which ones those are.
Verify
Array mapping is working end-to-end if:
| Check | Where to look |
|---|---|
| One scope per target array | Two [*] edges into the same target container produce one chip with 2 attrs, not two chips. |
| The badge matches the scenario | N→N for element-wise, N→1 for collapse, 1→N for construct. |
| The scope verified | Chip is green. Amber means the verifier flagged it for review; sky/blue means verification is still pending. |
| Overrides persist | Change a strategy, re-run Auto-map, and the chip still shows your choice plus the edited badge. |
Troubleshooting
No chips appear on the canvas.
The flag is off in this build — confirm NEXT_PUBLIC_MAPCRAFT_ARRAY_MAPPING=true
was present at build time and redeploy. Your scopes still exist server-side
and will render once the flag is on.
I drew a second array edge and no new chip appeared. By design: edges sharing the same target array container fold into the existing scope. Check the chip's attribute count — it should have incremented.
The chip is amber (needs review).
The verifier's cardinality guard flagged the scope — typically the rule's
output cardinality doesn't match the target (e.g. an array-typed target
receiving a single value). Revisit the strategy; collapse and construct
are the usual fixes for mismatched sides.
My collapse mode keeps showing sum but I wanted concat.
Pick concat in the collapse-mode dropdown — the change persists as a
manual override. If you instead re-ran Auto-map before overriding, the
ai-origin scope was regenerated with the recommendation; override it and
it will stick from then on.
What's next
- Map many fields to one target — the N→1 combine system that array scopes share their origin semantics with.
- MapCraft walkthrough — rules, versioning, and the DAG compile that consumes your scopes.
- FlowBridge walkthrough — run the compiled result against live data.