jq Cheatsheet

Paths and Indexing

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

Indexing forms

FormOnResult
.keyobjectthe value, null if absent
.["key"]objectsame, and the form that accepts an expression
."odd-key"objectneeded for -, spaces, digits, reserved words
.[0]arrayfirst element
.[-1]arraylast element
.[]array or objectevery value as a separate output
.[2:5]array or stringslice, end exclusive
.[$k]objectkey from a variable
jq -c '.["my-key"], ."2fa", .["a b"]'   # all three need quoting
jq -c --arg k name '.[$k]' data.json    # "api"
jq -c '.tags[-1]' data.json             # "prod"

Out-of-range array indexes are null, not errors. .[] on a string is an error, use explode or split("").

Slices

jq -c '.tags[1:]'     # ["prod"]
jq -c '.tags[:1]'     # ["web"]
jq -c '.tags[-1:]'    # ["prod"], last one
jq -c '"abcdef"[2:4]' # "cd", slices work on strings too
jq -c '.items[1:3] | map(.id)'   # [2,3]

Both bounds are optional and negative counts from the end. A slice always returns the same type it was given, an array from an array and a string from a string.

Recursive descent

.. emits the input and every value nested anywhere inside it. Pair it with a type filter, because it also emits containers.

jq -c '[.. | numbers]'                        # [1,10,2,20,3,30,3]
jq -c '[.. | .price? // empty]'               # [10,20,30]
jq -c '[.. | objects | select(has("price")) | .id]'   # [1,2,3]
FilterKeeps
numbers strings booleans nullsthat scalar type
arrays objects iterables scalarsthat shape
valueseverything except null

.. is recurse with no arguments. It is O(size of document), so on a large file prefer an explicit path.

paths and path

paths emits every path as an array of keys and indexes. path(f) emits the path that the filter f would take.

jq -c '[paths]'                     # [["name"],["tags"],["tags",0],...]
jq -c '[paths(scalars)] | length'   # 17, only leaf paths
jq -c '[paths(type=="number")]'     # [["items",0,"id"],["items",0,"price"],...]
jq -c '[path(.items[].price)]'      # [["items",0,"price"],...]
jq -c 'path(.a.b[0])' <<< 'null'    # ["a","b",0]

path does not evaluate the value, it records the route, which is what makes generic tools possible. leaf_paths was removed, write paths(scalars).

getpath, setpath, delpaths

The programmatic form of a path expression, taking the path as data.

jq -c 'getpath(["meta","count"])'            # 3
jq -c 'getpath(["x","y"])'                   # null, no error
jq -c 'setpath(["meta","count"]; 99) | .meta'   # {"count":99}
jq -c 'delpaths([["meta"],["tags"]]) | keys'    # ["items","name","version"]
FunctionSignature
getpath(PATHS)read, null for a missing route
setpath(PATHS; value)write, creating intermediate objects
delpaths([PATHS])delete several paths at once, deepest first
pathsevery path in the document
path(f)the path f navigates

delpaths takes a list of paths and handles the reindexing, which is why deleting several array elements needs it rather than repeated del.

del

jq -c 'del(.meta, .tags) | keys'       # ["items","name","version"]
jq -c 'del(.items[1]) | [.items[].id]' # [1,3], array is reindexed
jq -c 'del(.items[].cat) | .items'     # drops cat from every element
jq -c '.items[0] | del(.price, .cat)'  # {"id":1,"name":"a"}

del takes a path expression, not a value, so del(.items[] | select(.price > 15)) works and del(20) does not.

Assignment and update

OperatorMeaning
=set to the value on the right, evaluated against the original input
|=update, the right side receives the current value as .
+= -= *= /= %=arithmetic update, sugar for |= . + …
//=set only if the current value is null or false
jq -c '.meta.count = 9 | .meta'                   # {"count":9}
jq -c '.meta.count |= . + 1 | .meta'              # {"count":4}
jq -c '.items[].price *= 2 | [.items[].price]'    # [20,40,60]
jq -c '.tags += ["dev"] | .tags'                  # ["web","prod","dev"]
jq -c '.newkey = 1 | keys'                        # key is created
jq -c '(.items[] | select(.cat == "x") | .price) = 0 | [.items[].price]'
# [0,20,0]

The left side of any assignment must be a path expression, so (.a + 1) = 2 is an error. The last example is the idiom worth memorizing, a select inside the path updates only the matching elements and leaves the array shape alone.

= versus |= matters when the right side reads the document. .a = .b copies .b, while .a |= .b sets .a to .a.b.