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
| Form | On | Result |
|---|---|---|
.key | object | the value, null if absent |
.["key"] | object | same, and the form that accepts an expression |
."odd-key" | object | needed for -, spaces, digits, reserved words |
.[0] | array | first element |
.[-1] | array | last element |
.[] | array or object | every value as a separate output |
.[2:5] | array or string | slice, end exclusive |
.[$k] | object | key 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]
| Filter | Keeps |
|---|---|
numbers strings booleans nulls | that scalar type |
arrays objects iterables scalars | that shape |
values | everything 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"]
| Function | Signature |
|---|---|
getpath(PATHS) | read, null for a missing route |
setpath(PATHS; value) | write, creating intermediate objects |
delpaths([PATHS]) | delete several paths at once, deepest first |
paths | every 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
| Operator | Meaning |
|---|---|
= | 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.