Column Mapping
The problem
Two systems export the same data with different column names. A bank statement
calls a field counterparty. The ERP calls it entity_name. A legacy CRM
uses customer_id. The new platform uses cust_id.
Without column mapping, Reconlify sees these as unrelated columns. It cannot
match counterparty to entity_name or compare their values. Rows that are
actually identical appear as entirely different.
Example datasets
You are reconciling a payment processor export against your internal ledger. The data is the same, but every column has a different name.
payments.csv (source)
pay_id,merchant,pay_amount,pay_currency,pay_date,method
P-3001,Acme Corp,4500.00,USD,2026-02-15,wire
P-3002,Globex Inc,1200.50,EUR,2026-02-16,ach
P-3003,Initech Ltd,8900.00,GBP,2026-02-16,wire
P-3004,Wayne Ent,320.75,USD,2026-02-17,cardledger.csv (target)
transaction_id,vendor_name,amount,currency,txn_date,payment_method
P-3001,Acme Corp,4500.00,USD,2026-02-15,wire
P-3002,Globex Inc,1200.50,EUR,2026-02-16,ach
P-3003,Initech Ltd,8900.00,GBP,2026-02-16,wire
P-3004,Wayne Ent,320.75,USD,2026-02-17,cardThe records are identical. But a raw diff would flag every row because the column headers do not match.
The column mapping config
type: tabular
source: payments.csv
target: ledger.csv
keys:
- pay_id
column_mapping:
pay_id: transaction_id
merchant: vendor_name
pay_amount: amount
pay_currency: currency
pay_date: txn_date
method: payment_methodEach entry in column_mapping is a pair: the left side is the source column
name, the right side is the target column name it corresponds to.
With this mapping, Reconlify knows that pay_id in the source corresponds to
transaction_id in the target, merchant corresponds to vendor_name, and
so on. It pairs the values and compares them correctly.
Logical column names
Column mapping introduces a concept that affects how you write the rest of your config: the logical name.
The logical name is the source-side column name. Once you define a mapping,
you use the logical name everywhere else in the config — keys, tolerance,
string rules, filters, include/exclude columns. You never reference target
column names outside of column_mapping.
Here is how the three names relate for each column in this example:
| Source column | Target column | Logical name |
|---|---|---|
pay_id |
transaction_id |
pay_id |
merchant |
vendor_name |
merchant |
pay_amount |
amount |
pay_amount |
pay_currency |
currency |
pay_currency |
pay_date |
txn_date |
pay_date |
method |
payment_method |
method |
The logical name is always the source column name. The mapping tells Reconlify how to find the corresponding column in the target file.
This means the rest of the config reads naturally in source terms:
keys:
- pay_id # not "transaction_id"
tolerance:
pay_amount: 0.05 # not "amount"
string_rules:
merchant: # not "vendor_name"
- trim
- case_insensitiveIf a column has the same name in both files, you do not need to map it. Unmapped columns are looked up by their source name in the target file automatically.
Result interpretation
With the mapping above, Reconlify matches all four rows by pay_id and
compares every mapped column pair. The report summary:
{
"source_rows": 4,
"target_rows": 4,
"missing_in_target": 0,
"missing_in_source": 0,
"rows_with_mismatches": 0,
"mismatched_cells": 0
}Zero mismatches. The data is identical despite completely different column names.
If the target had a different value — say vendor_name was "ACME CORP"
instead of "Acme Corp" for P-3001 — the report sample would show:
{
"key": { "pay_id": "P-3001" },
"columns": {
"merchant": {
"source": "Acme Corp",
"target": "ACME CORP"
}
}
}The report uses the logical name (merchant) in the output. The key also uses
the logical name (pay_id). You can trace every difference back to the source
schema without needing to remember target column names.
To handle that casing difference, add a string rule — using the logical name:
string_rules:
merchant:
- case_insensitiveMapping with generated columns
Column mapping works with source-side normalization to bridge structural differences, not just naming differences.
The source stores names in two columns. The target has a single combined field with a different name:
column_mapping:
full_name: customer_full_name
normalization:
full_name:
- op: concat
args: [first_name, " ", last_name]
- op: trim
ignore_columns:
- first_name
- last_nameNormalization generates full_name on the source side. Column mapping tells
Reconlify that full_name corresponds to customer_full_name in the target.
The generated value is compared against the target column.
The logical name for the generated column is full_name — the name you chose
in the normalization section. It works like any other logical name in keys,
tolerance, or string rules.
How mapping interacts with other config sections
Every config section uses logical names. Column mapping only affects how Reconlify looks up the target column.
| Config section | Uses logical names? | Example |
|---|---|---|
keys |
Yes | keys: [pay_id] matches against transaction_id in target |
compare.include_columns |
Yes | include_columns: [pay_amount] includes the mapped target column |
compare.exclude_columns |
Yes | exclude_columns: [pay_date] excludes the mapped target column |
ignore_columns |
Yes | ignore_columns: [pay_date] ignores the mapped target column |
tolerance |
Yes | tolerance: { pay_amount: 0.05 } applies to the mapped pair |
string_rules |
Yes | string_rules: { merchant: [trim] } normalizes both sides |
normalization |
Yes | Generated column names are logical names |
If a column is not listed in column_mapping, Reconlify assumes the target
uses the same name as the source. You only need to map columns that differ.
Limitations
- No many-to-one mapping. Each logical column maps to exactly one target column. You cannot map two source columns to the same target column.
- No one-to-many mapping. Each target column can only be mapped from one logical column.
- Rename only. Column mapping is a pure rename on the target side, not a transform. To transform values, use source-side normalization.
- Runtime validation. Reconlify checks that mapped target columns exist when the file is loaded. If a mapped column is missing from the target file, the error appears at runtime.
Related docs
- Normalization and Rules — create derived columns on the source side that can be mapped to target columns
- Financial Reconciliation — example using column mapping with tolerance and string rules
- Data Migration — example using column mapping with composite keys and normalization
- YAML Config Reference — full configuration reference including column mapping syntax