jq Cheatsheet

Selecting and Filtering

Use this jq reference while you build software engineering projects, review code, or refresh the syntax you reach for most.

select

select(f) emits its input unchanged when f is truthy, and nothing otherwise. It is the filter, not a loop.

jq -c '[.items[] | select(.price > 15)]'
# [{"id":2,...,"price":20,...},{"id":3,...,"price":30,...}]
jq -c '[.items[] | select(.cat == "x" and .price > 15) | .id]'   # [3]
jq -c '[.items[] | select(.cat == "x" or .id == 2) | .id]'       # [1,2,3]
jq -c '[.items[] | select(.name | test("^[ab]$")) | .id]'        # [1,2]
jq -c '[.items[] | select(has("cat")) | .id]'                    # [1,2,3]

Truthiness in jq is narrow: only false and null are falsy. 0, "", [], and {} are all truthy, which is the opposite of most languages.

jq -cn '[0, "", [], {}] | map(if . then "truthy" else "falsy" end)'
# ["truthy","truthy","truthy","truthy"]

Negating a condition

not is a filter with no arguments, so it goes on the right of a pipe.

jq -c '[.items[] | select(.cat == "x" | not) | .id]'    # [2]
jq -c '[.items[] | select(.price > 15 | not) | .id]'    # [1]
jq -c '[.items[] | select((.tag // null) == null) | .id]'

not takes no argument, so select(not .x) parses but means "negate the whole input, then index that boolean", which fails at runtime with a confusing message. There is no prefix ! either. Pipe into not, or compare with == false.

Comparison and boolean operators

OperatorNotes
== !=deep structural equality, works on arrays and objects
< <= > >=total order across all types, never an error
and orshort-circuit, lower precedence than comparison
nota filter, f | not

jq defines a single total order over every type, so a comparison never fails:

null < false < true < numbers < strings < arrays < objects
jq -cn '1 < "a", [1] < [2], {a:1} == {a:1}, [1,2] == [1,2]'
# true true true true

Sorting a mixed array therefore works, and null always sorts first. That is also why a missing key sorts to the top with sort_by.

has, in, contains, inside

FilterAsks
has("k")does this object have key k
has(0)does this array have index 0
"k" | in($obj)the same question, arguments flipped
contains(x)does the input contain x, recursively
inside(x)the same question, arguments flipped
jq -c '.meta | has("count"), has("nope")'      # true false
jq -c '.tags | has(0), has(9)'                 # true false
jq -c '.meta as $m | "count" | in($m)'         # true
jq -c '.name | contains("pi")'                 # true, substring
jq -c '.tags | contains(["web"])'              # true, subset
jq -c '.meta | contains({count: 3})'           # true, submatch
jq -c '.tags as $t | ["web"] | inside($t)'     # true

in and inside take the container as an argument, so it is evaluated against the current input, not the original document. Bind it to a variable first, as above, or the path will resolve against the wrong thing.

has checks presence, .k != null checks value. A key present with a null value passes has and fails the second test.

any and all

Three arities each. The one-argument form is the one you want most of the time.

FormMeaning
any / allover an input array of values, truthiness
any(cond) / all(cond)over an input array, apply cond to each
any(gen; cond)over the outputs of gen, apply cond
jq -c '[.items[].price] | any(. > 25)'      # true
jq -c '[.items[].price] | all(. > 5)'       # true
jq -c 'any(.items[]; .cat == "y")'          # true
jq -c 'all(.items[]; .price > 5)'           # true
jq -cn '[] | any'                           # false
jq -cn '[] | all'                           # true, vacuously

Both short-circuit. any stops at the first true, all at the first false.

//, the alternative operator

a // b emits every truthy output of a, and only if there are none does it emit b.

jq -c '.nope // "dflt"'          # "dflt"
jq -cn 'null // 1'               # 1
jq -cn 'false // 1'              # 1
jq -cn '0 // 1'                  # 0, zero is truthy
jq -cn '"" // 1'                 # "", empty string is truthy
jq -cn 'empty // 9'              # 9, no outputs at all
jq -cn '(1, null, 2) // 9'       # 1 then 2, the nulls are dropped

The last line is the behavior people miss. // is not a null-coalescing operator on a single value, it is a filter over a stream. .a? // "x" is the idiom for "this field, or a default, and do not error if the parent is the wrong type".

jq -c '.items[] | {id, tags: (.tags // [])}'   # never null
jq -c '[.. | .price? // empty]'                # collect, ignore failures

try, catch, and isempty

jq -c '[.items[] | try (.price | tostring) catch "?"]'   # ["10","20","30"]
jq -cn '[(1, "a", 2) | try (. + 1) catch "skip"]'        # [2,"skip",3]
jq -cn '[(1, "a", 2) | (. + 1)?]'                        # [2,3]
jq -c 'isempty(.items[] | select(.price > 99))'          # true
jq -c 'first(.items[] | select(.price > 15)) | .id'       # 2
FormOn error
try femit nothing, same as f?
try f catch grun g with the error message as .
f?emit nothing, terser
isempty(f)true when f produces no output
error("msg")raise, exit 5
errorre-raise the input as an error

try catches errors, not empty output. A filter that legitimately produces nothing still produces nothing, which is why isempty exists as a separate test.