Skip to content

data.map

Transform each row. Add columns, rename fields, compute values.

Input: Table | Output: Table

Add a full_name column from first and last name:

nodes:
add-name:
type: data.map
config:
include_original: true
fields:
full_name: "{{ first_name }} {{ last_name }}"
FieldTypeRequiredDescription
fieldsmapyesOutput column name to template expression
include_originalbooleannoKeep all input columns alongside mapped fields. Default: false

When include_original is true, the output contains every input column plus the mapped fields. If a mapped field name matches an existing column, the mapped value replaces it. When false, the output contains only the mapped fields.

Expressions use {{ ... }} delimiters. Inside them, you reference columns by name and apply operations.

fields:
email_copy: "{{ email }}"
fields:
total: "{{ price * quantity }}"
margin: "{{ revenue - cost }}"
tax: "{{ subtotal * 0.19 }}"

Multiple {{ }} blocks in one expression produce string concatenation:

fields:
full_name: "{{ first_name }} {{ last_name }}"
greeting: "Hello {{ first_name }}!"

Filters transform values. Syntax: {{ field | filter }}.

FilterResult typeDescription
lowerstringLowercase
upperstringUppercase
trimstringStrip whitespace
lengthnumberString length
roundnumberRound to nearest integer
ceilnumberRound up
floornumberRound down
absnumberAbsolute value
to_stringstringCast to string
to_numbernumberCast to number (null on failure)
strip_currencynumberRemove currency symbols, parse as number
extract_numbernumberPull first numeric value from string
parse_date_dmydateParse dd/mm/yyyy
parse_date_mdydateParse mm/dd/yyyy
parse_date_ymddateParse yyyy-mm-dd
fields:
email_lower: "{{ email | lower }}"
price_cents: "{{ price_text | strip_currency }}"
joined: "{{ join_date | parse_date_dmy }}"

replace and regexp_replace take arguments:

fields:
clean_phone: '{{ phone | replace("+", "00") }}'
no_tags: '{{ body | regexp_replace("<[^>]+>", "") }}'
fields:
display_name: '{{ nickname ?? "Anonymous" }}'
fields:
tier: '{{ score > 0.7 ? "high" : "low" }}'

For column names with special characters, use double quotes inside the expression:

fields:
amount: '{{ "Debit/Credit" | to_number }}'
nodes:
enrich-orders:
type: data.map
config:
include_original: true
fields:
total: "{{ price * quantity }}"
discount_amount: "{{ total * discount_pct }}"
status_label: '{{ status == "paid" ? "Paid" : "Pending" }}'
nodes:
clean-contacts:
type: data.map
config:
fields:
name: "{{ first_name }} {{ last_name }}"
email: "{{ email | lower | trim }}"
score: "{{ raw_score | round }}"

Since include_original defaults to false, the output contains only name, email, and score.

nodes:
normalize:
type: data.map
config:
include_original: true
fields:
email: "{{ email | lower | trim }}"
domain: '{{ email | lower }}'
name_upper: "{{ name | upper }}"
nodes:
parse-amounts:
type: data.map
config:
include_original: true
fields:
amount: "{{ amount_text | strip_currency }}"
quantity: "{{ qty_string | to_number }}"
label: "{{ id | to_string }}"

Casts use TRY_CAST internally — unparseable values become null instead of errors.

nodes:
parse-dates:
type: data.map
config:
include_original: true
fields:
signup_date: "{{ raw_date | parse_date_mdy }}"
birth_date: "{{ dob | parse_date_dmy }}"
nodes:
compute-pricing:
type: data.map
config:
include_original: true
fields:
final_price: '{{ is_member == true ? price * 0.9 : price }}'
tax: "{{ price * 0.19 | round }}"

NULL propagation. Any arithmetic or filter applied to a NULL value produces NULL. Use the coalesce operator (??) to provide fallback values.

Type changes. Mapped fields can change the type of an existing column. If score was a string and you map it with to_number, the output schema reflects the new type.

Empty input. An empty input table produces an empty output table with the mapped schema.