Overview

jq is the standard tool for slicing, filtering, and transforming JSON on the command line. This card covers the expressions you reach for every day. For querying JSON inside Postgres, see postgres-jsonb. For using jq with API responses, see curl-flags.

All examples use this input unless stated otherwise:

[
  {"name": "alice", "age": 30, "tags": ["admin", "user"]},
  {"name": "bob",   "age": 25, "tags": ["user"]},
  {"name": "carol", "age": 35, "tags": ["admin"]}
]

Selectors

Access fields and array elements.

ExpressionResult
.Identity; pass input through unchanged.
.nameField name from an object.
.["user-name"]Field with special characters in name.
.[0]First element of an array.
.[-1]Last element.
.[2:5]Slice from index 2 to 4.
.[]Iterate array or object values.
.foo.barChained field access.
.foo?.barOptional: suppress errors if .foo is null or missing.
keysArray of object keys.
valuesArray of object values.
lengthLength of array, string, or object.
has("name")True if key exists.
in({"a":1})True if the input is a key in the object.

Use ? to suppress null errors when a field might not exist.

Filters and transforms

Shape and transform data.

ExpressionResult
.[].name"alice", "bob", "carol" (one per line).
[.[].name]["alice","bob","carol"] (collect into array).
{n: .name, a: .age}Build a new object.
[.[] | {n: .name}]Map to new objects.
select(.age > 28)Keep only matching elements.
[.[] | select(.age > 28)]Filter array.
sort_by(.age)Sort array by field.
sort_by(.age) | reverseSort descending.
group_by(.age)Group into arrays by field value.
unique_by(.name)Deduplicate by field.
min_by(.age) / max_by(.age)Min or max element.
addSum numbers, concatenate strings/arrays, merge objects.
flattenRecursively flatten nested arrays.
to_entriesConvert object to [{key, value}] pairs.
from_entriesConvert [{key, value}] pairs back to object.
with_entries(.value += 1)Map over object entries.

Piping and composition

Chain expressions with |; assign intermediate values with as.

ExpressionResult
.[] | .nameStream names.
.[] | select(.age > 28) | .nameFiltered names.
[.[] | .age] | add / lengthAverage age.
. as $input | ...Bind input to variable for reuse.
(.a, .b)Multiple outputs from one expression.
if .age > 30 then "senior" else "junior" endConditional.
try .foo catch "missing"Handle errors gracefully.
@base64Base64-encode a string.
@jsonSerialize to JSON string.
@csvFormat an array as CSV.
@tsvFormat an array as TSV.
@uriURL-encode a string.
@htmlHTML-escape a string.

map and select patterns

map applies an expression to every element; select filters elements.

PatternResult
map(.age | . * 2)Double every age.
map(select(.age > 28))Filter array inline (equivalent to [.[] | select(...)]).
map(.tags[])Flatten nested arrays one level.
[.[] | .tags[]]All tags across all users.
[.[] | .tags[] | select(. == "admin")]All occurrences of "admin".
map(select(.tags | contains(["admin"])))Users with “admin” tag.
map(.name) | sort | uniqueSorted unique names.
reduce .[] as $u (0; . + $u.age)Sum ages with reduce.

Common one-liners

Ready-to-paste expressions for frequent tasks.

Taskjq expression
Pretty-printjq '.'
Compact outputjq -c '.'
Raw string outputjq -r '.name'
Count itemsjq 'length'
First matchjq 'first(.[] | select(.age > 28))'
Pluck fieldsjq '[.[] | {name, age}]'
Merge two objectsjq -n '$a * $b' --argjson a '{"x":1}' --argjson b '{"y":2}'
Pass shell varjq --arg name "$NAME" '.[] | select(.name == $name)'
Null fallbackjq '.foo // "default"'
Recursive descentjq '.. | .id? | values'

Use jq -r when the output feeds a shell variable; without -r the value is quoted JSON.

Common gotchas

  • .foo returns null for a missing key, not an error. Use has("foo") or if .foo then to distinguish missing from null.
  • // is the alternative operator: .foo // "default" returns "default" only when .foo is false or null. It does not protect against missing keys returning null.
  • .[].foo streams values; wrap in [...] to collect them into an array. Forgetting the brackets means the output is multiple JSON values, not a JSON array.
  • jq -r on an array output still prints the array brackets. Use jq -r '.[]' to get raw lines.
  • The pipe inside a map(...) applies to each element; a pipe outside map applies to the whole array. map(. + 1) adds 1 to each; map(.) | add concatenates or sums the whole array.
  • sort_by requires comparable types. Mixed-type arrays (numbers and strings) cause an error.