jqcommand-linejsonfilteringcheat-sheetguides

jq Cheat Sheet: The Recipes You Actually Use

·9 min read·Tool Guides

jq is JSON's command line

jq is a small, fast command-line tool for slicing, filtering, and reshaping JSON — think of it as sed/awk for JSON. It reads JSON on stdin, applies a filter you write, and prints JSON (or raw text) on stdout, which makes it the go-to for exploring API responses, log files, and config from a terminal. This is the recipe-focused companion to the concept-level JSONPath guide; to try any filter below interactively, paste your JSON into the jq playground.

Every example assumes this input in data.json:

json
{
  "users": [
    { "name": "Ada",  "age": 36, "role": "admin",  "tags": ["a", "b"] },
    { "name": "Ravi", "age": 29, "role": "member", "tags": ["b"] },
    { "name": "Mei",  "age": 41, "role": "admin",  "tags": [] }
  ]
}

The basics: access and pipe

The filter . is the identity — it returns the input unchanged, which is why jq . data.json is the fastest way to pretty-print any JSON. From there you drill in with dots and pipe (|) filters together:

bash
jq '.'                     # pretty-print the whole document
jq '.users'                # the users array
jq '.users[0]'             # first user object
jq '.users[0].name'        # "Ada"
jq '.users | length'       # 3  — pipe the array into length

Iterate an array with .[]

.[] explodes an array into a stream of its elements — the heart of most jq one-liners. Pipe that stream into another filter to act on each element:

bash
jq '.users[].name'                 # "Ada"  "Ravi"  "Mei"  (one per line)
jq '.users[] | .role'              # "admin"  "member"  "admin"
jq '[.users[].name]'               # ["Ada","Ravi","Mei"] — wrap the stream back into an array

The bracket-wrapping trick — [ ... ] around a stream — is how you collect results back into a single array.

Filter with select()

select(condition) passes through only the elements where the condition is true. Combine it with .[] to filter an array:

bash
# users older than 30
jq '.users[] | select(.age > 30)'

# just the names of admins
jq '.users[] | select(.role == "admin") | .name'    # "Ada"  "Mei"

# users who have any tags
jq '.users[] | select(.tags | length > 0)'

Transform with map() and object construction

map(f) applies a filter to every element of an array and returns a new array. Build new objects with { key: filter } to reshape data:

bash
# pull one field from each element
jq '.users | map(.name)'                       # ["Ada","Ravi","Mei"]

# reshape each user into a smaller object
jq '.users | map({ person: .name, admin: (.role == "admin") })'

# add a computed field
jq '.users | map(. + { senior: (.age >= 40) })'

Aggregate: group_by, sort_by, unique, add

The recipes that replace a script:

bash
jq '.users | group_by(.role)'                  # array of arrays, grouped by role
jq '[.users[].age] | add'                      # 106  — sum of ages
jq '[.users[].age] | add / length'             # average age
jq '.users | sort_by(.age)'                    # ascending by age
jq '.users | sort_by(-.age)'                   # descending
jq '[.users[].role] | unique'                  # ["admin","member"]
jq '.users | max_by(.age) | .name'             # "Mei"

Keys, values, and to_entries

Inspect and iterate object shapes:

bash
jq '.users[0] | keys'                          # ["age","name","role","tags"]
jq '.users[0] | to_entries'                    # [{"key":"name","value":"Ada"}, ...]
jq '.. | .name? // empty'                       # every "name" anywhere (recursive descent)

.. is recursive descent — it walks every value at every depth, handy when you don't know how deeply a field is nested. The // empty swallows the misses, and // in general is the alternative operator ("use the left value, or the right if the left is null/false").

Raw and CSV output for pipelines

By default jq prints JSON (strings get quotes). -r prints raw strings — essential when feeding other shell tools — and @csv/@tsv format arrays as delimited rows:

bash
jq -r '.users[].name'                          # Ada  Ravi  Mei   (no quotes)

# turn objects into CSV rows
jq -r '.users[] | [.name, .age, .role] | @csv' # "Ada",36,"admin" ...

That last recipe is a one-line JSON-to-CSV converter for flat records.

Working with NDJSON and multiple files

jq reads a stream of JSON values, so it handles NDJSON (one object per line) natively — no array needed:

bash
# process each line of a log file, keep errors
jq -c 'select(.level == "error")' app.ndjson   # -c = compact, one line out per match

# slurp separate values into one array with -s
jq -s 'add' part1.json part2.json

-c (compact) keeps output as one line per value — the right form for producing NDJSON — and -s (slurp) reads the entire input into a single array first.

The handful worth memorizing

GoalFilter
Pretty-printjq .
A field.a.b
Every array element.arr[]
Filter.arr[] \| select(.x > 1)
Reshape each.arr \| map({k: .v})
Sum[.arr[].n] \| add
Raw text outjq -r
CSV row[.a, .b] \| @csv

Master those eight and you can answer most "get X out of this JSON" questions in one line. For the query-language view of the same problems see the JSONPath guide and the JSONPath tester; to experiment with jq itself, use the jq playground.

Frequently asked questions

jq is a command-line tool for filtering, transforming, and reshaping JSON — effectively sed/awk for JSON. It's used to explore API responses, process log files, extract fields, and convert JSON to other formats directly from the terminal or inside shell scripts.

Use dot access: jq '.user.name' returns the name inside user. For every element of an array, use .[], e.g. jq '.users[].name' prints each user's name.

Iterate with .[] and pipe into select: jq '.users[] | select(.age > 30)' keeps only users older than 30. Add another pipe to pull a field, e.g. | .name.

Use the -r (raw) flag: jq -r '.users[].name' prints the strings without quotes, which is what you want when feeding the output into other shell commands.

Yes. jq reads a stream of JSON values, so it processes NDJSON natively — jq 'select(.level=="error")' app.ndjson filters each line. Use -c for compact one-line output and -s to slurp all values into a single array.

Try JSONKit — JSON Developer Tools

Format, validate, convert and diff JSON. Instant, accurate and 100% private.