Paste JSON and write a filter to begin.
The tool above runs jq against your JSON in the browser. The reference below is a working library of jq filters organized by category, plus the CLI flag table and the common-failure list. Every snippet is copy-paste ready for use here or in your terminal.
Identity
.Passes input through unchanged. The trivial filter, useful for confirming what jq sees.
Field access
.name
.user.email
.["weird key with spaces"]Dot-notation walks an object. The square-bracket form is the escape hatch for keys that aren't valid identifiers.
Optional field access
.user.email?Returns null instead of erroring if .user is missing or not an object.
Array indexing
.[0]
.[-1]
.[2:5]Indexing, last element, and slicing. Slices are half-open like Python.
Array iteration
.[]Yields each element as a separate output. The most-used operator in real jq filters.
Pipe
.users | .[0] | .nameOutput of one filter becomes input to the next. Behaves like a shell pipe.
Map a transformation across each element
map(.price * 1.1)Equivalent to [.[] | (.price * 1.1)] but reads better.
Filter elements by predicate
map(select(.active == true))
map(select(.price >= 100))select(cond) keeps the input when cond is truthy and drops it otherwise.
Sort
sort
sort_by(.created_at)
sort_by(.priority) | reverseDescending sort is sort_by(...) | reverse; there is no sort_by_desc.
Group by
group_by(.category)Returns an array of arrays. Each inner array holds elements that share the same .category value.
Count occurrences (group and tally)
group_by(.status) | map({status: .[0].status, count: length})The canonical "group and count" idiom. Produces [{status: "open", count: 12}, ...].
Unique values
[.[] | .country] | unique
unique_by(.email)unique sorts then dedupes. unique_by lets you specify the key.
Flatten nested arrays
flatten
flatten(1)flatten collapses all nesting; flatten(n) collapses only n levels.
Min / max by key
min_by(.age)
max_by(.score)Length / count
length
[.[] | select(.active)] | lengthlength returns array length, object key count, or string length depending on input type.
Extract specific keys
{name, email}
{name: .name, role: .user_role}Shorthand {name} is sugar for {name: .name}.
Add or update a field
. + {processed_at: now}
.status = "shipped"+ merges objects (right wins on conflict). = assigns in place.
Delete a field
del(.password)
del(.users[].password)Convert object to entries (key/value pairs)
to_entriesTurns {a: 1, b: 2} into [{key: "a", value: 1}, {key: "b", value: 2}]. Required for any filter that iterates over keys.
Convert entries back to object
from_entriesInverse of to_entries.
Filter object by key or value
with_entries(select(.value > 10))
with_entries(select(.key | startswith("user_")))with_entries(f) is sugar for to_entries | map(f) | from_entries.
Get all keys
keys
keys_unsortedkeys returns sorted; keys_unsorted preserves insertion order.
Check for presence
has("email")
.users[0] | has("admin")Split a string
split(",")
split(" *, *"; "g")The second form uses a regex pattern.
Join an array of strings
join(", ")Case conversion
ascii_downcase
ascii_upcaseTrim whitespace
gsub("^\\s+|\\s+$"; "")jq has no built-in trim; this regex is the standard idiom.
Regex match
test("^[a-z]+$")
match("[0-9]{3}-[0-9]{4}")
capture("(?<area>[0-9]{3})-(?<line>[0-9]{4})")test returns a boolean, match returns positions, and capture returns named groups as an object.
Substring replacement
sub("foo"; "bar")
gsub("\\bfoo\\b"; "bar")sub replaces the first match; gsub replaces all.
String length
lengthOn a string, returns the number of Unicode codepoints.
Numeric formatting
. * 100 | floor
(. * 100 | round) / 100
tonumber
tostringjq has no printf; round to N decimals by multiplying, rounding, then dividing.
Pivot: turn a list into a lookup keyed by ID
[.[] | {(.id | tostring): .}] | add(.id | tostring) is needed because object keys must be strings.
Group, count, sort by count, take top N
group_by(.event)
| map({event: .[0].event, count: length})
| sort_by(-.count)
| .[:10]The top-10-by-frequency report. Sorting by -.count gives descending order without needing reverse.
Sum a field across all elements
[.[] | .amount] | add
map(.amount) | addFilter out null and empty values
map(select(.email != null and .email != ""))Conditional output
if .status == "error" then .message else empty endempty produces no output. Use it as the "drop this element" alternative inside if.
Walk a tree and transform every value
walk(if type == "string" then ascii_downcase else . end)walk recurses into every value in any nested structure.
Pretty-print a date field
.created_at | fromdate | strftime("%Y-%m-%d")fromdate parses ISO 8601 strings to epoch seconds; strftime formats them.
Diff two objects (keys in A, missing from B)
(.a | keys) - (.b | keys)Convert an array of objects to NDJSON
.[]With -c enabled, this prints each object as one compact line, which is the NDJSON format.
Convert objects to CSV rows
.[] | [.name, .email, .signup_date] | @csvCombined with -r, this produces lines suitable for spreadsheet import.
Each is a prefix applied to an array of values:
.users | map([.name, .email]) | .[] | @csvWith -r enabled, that prints "Ada","ada@example.com" on each line.
kubectl and Postman. Good for read-only field extraction; weak at transformation.-c flag produces it./users/0/email). Used by JSON Patch and OpenAPI.