Cheat sheet
Everything the CLI understands, on one page. A pipeline is a JSON array of steps run in order; each step is an object with an op and its parameters. Expressions are plain JavaScript with the current row as item and its index as i. Every heading is a link, so you can send someone straight to #join.
$ transmute data.json --pipe '[{"op":"filter","expr":"item.age > 18"},{"op":"sort","by":"name"}]' -o csv
Operations
Every operation takes an array of rows and returns an array of rows, so any order works. Row-shaping steps (pick, rename, add) are usually cheapest last, after filtering.
filter expr
Keep the rows where the expression is true. Strings compare with ===; numbers from CSV are already numbers, values from XML are strings.
{"op":"filter","expr":"item.status === \"shipped\" && item.total > 100"}
map expr
Replace each row with the value of the expression. Wrap object literals in parentheses. For adding a field or two, add is shorter.
{"op":"map","expr":"({ id: item.id, name: item.first + ' ' + item.last })"}
pick fields
Keep only the listed columns, in the order given. Missing fields are skipped rather than filled with null.
{"op":"pick","fields":["id","name","email"]}
omit fields
Drop the listed columns and keep everything else.
{"op":"omit","fields":["password","internal_note"]}
sort by, dir
Order by one field. Numbers sort numerically, everything else with locale-aware string comparison. dir is asc (default) or desc; rows missing the field go last.
{"op":"sort","by":"total","dir":"desc"}
unique by
Remove duplicates. With by, the first row per value of that field wins; without it, whole rows are compared.
{"op":"unique","by":"email"}
group by
One row per distinct value with key, count and the grouped rows under items. Follow with pick to get a plain count table.
{"op":"group","by":"city"},
{"op":"pick","fields":["key","count"]}
count
Replace the rows with a single {"count": n}. Useful at the end of a filter.
{"op":"count"}
head n
Keep the first n rows (default 10). Good for a quick look at a large file.
{"op":"head","n":5}
tail n
Keep the last n rows (default 10).
{"op":"tail","n":20}
rename mapping
Rename columns with an old-to-new mapping. Columns not in the mapping keep their names and their position.
{"op":"rename","mapping":{"qty":"quantity","cust":"customer"}}
flatten field
One output row per element of an array field, with the parent's other fields repeated. Object elements are merged into the row; scalar elements land under the field name. Rows without the array pass through unchanged.
{"op":"flatten","field":"items"}
add fields
Add computed columns. Each value is an expression evaluated per row; an expression that throws yields null instead of stopping the run.
{"op":"add","fields":{"total":"item.price * item.qty","vat":"Math.round(item.price * item.qty * 0.25)"}}
join on, with, keep, prefix
Merge a second row set on a shared key, compared as strings. Default is an inner join; "keep":"left" keeps unmatched rows. Joined fields can get a prefix so they never overwrite existing columns. Splice a file in from the shell with "with":$(cat other.json).
{"op":"join","on":"sku","keep":"left","prefix":"stock_","with":[{"sku":"A1","stock":42}]}
CLI flags
The first non-flag argument is the input file; leave it out to read stdin. With no --pipe and no --output you get a table preview of the file.
--pipe, -p <json>
The pipeline as a JSON array. Single-quote it in bash, zsh, Git Bash and WSL. In PowerShell use the stop-parsing token --% and escape inner quotes as \".
$ transmute orders.json --pipe '[{"op":"head","n":3}]'
--format, -f json | csv | yaml | xml
Force the input format. Normally detected from the file extension or, for stdin, from the content.
$ curl -s https://api.example.com/users | transmute --format json -o csv
--output, -o json | csv | yaml | xml | table | sql
The output format. Default is table, an aligned view for the terminal. Redirect with > to write a file.
$ transmute data.yaml -o json > data.json
--table <name>
Table name for SQL output (default my_table). Column names come from the keys of all rows combined.
$ transmute users.csv -o sql --table users > seed.sql
--help, -h
Print the usage text with the same examples as this page.
$ transmute --help
Formats
What goes in, what comes out, and the coercions on the way.
JSON read, write
An array of objects is the native shape. A single object is treated as one row. Output is pretty-printed with two spaces.
CSV read, write
First line is the header. Values are coerced the way a spreadsheet would: 32 becomes a number, true a boolean, an empty cell an empty string. Quoted fields with commas and doubled quotes are handled. Nested objects do not survive a CSV round-trip, so flatten first.
YAML read, write
Lists of flat records and simple key–value documents. Not supported: anchors, multi-line strings, deeply nested maps. JSON is valid YAML, so a .yaml file that is really JSON works too.
XML read, write
Reading: the root's children become rows, child elements become fields, repeated children become arrays. All values are strings, so wrap them in Number() in expressions. Writing: <data> root with one <item> per row, values escaped.
SQL write only
One INSERT statement with a row per record. Numbers stay bare, strings are single-quoted with quotes doubled, booleans become TRUE/FALSE, empty and null become NULL. Works as-is in Postgres, MySQL and SQLite.
table write only
An aligned text table for reading in the terminal. It is the default output when no other is given, and not meant to be parsed.
Expressions
Used by filter, map and add. They are compiled with new Function, so anything valid inside a JavaScript return (…) works: item.price * 1.25, item.name.toLowerCase().includes('vind'), Number(item.age) >= 18, new Date(item.created) > new Date('2026-01-01'). i is the row index, starting at 0. Expressions run with the same trust as the CLI itself; do not run pipelines you have not read.
From your own code
The engine is the npm package's main export, with no dependencies.
const { run, parsers, serializers, operations, detectFormat } = require('@mahope/transmute');
const { data, text, error } = run(csvText, 'csv', [{ op: 'filter', expr: 'item.age > 18' }], 'json');