Basics
Identity filter.
Select a single field.name
Select by field value.[] | select(.status == "active")
Access nested field.config.database.host
Pipe chain.users[] | select(.role == "admin") | .name
Arrays
Flatten nested arrays[.[][]]
Map and transform[.items[] | {id, upper: (.name | ascii_upcase)}]
Sort by fieldsort_by(.score) | reverse
First and last elements{first: first, last: last}
Objects
Rename keys.[] | {user: .name, mail: .email}
to_entries pivotto_entries | map({(.key): .value})
Merge objects.[0] * .[1]
Extract keys and values{keys: keys, values: [.[]]}
String & Math
Split and join strings.path | split("/") | map(select(. != "")) | join(" > ")
String interpolation.[] | "\(.name) scored \(.score) points"
Sum an array of numbers.prices | add
Regex test[.[] | select(test("@"))]
Real-World Patterns
Group by + countgroup_by(.type) | map({type: .[0].type, count: length})
Unique values[.[].tags[]] | unique | sort
Extract from API response.data.results[] | {id, name, status}
JSON to CSV rows.[] | [.name, (.age | tostring), .city] | join(",")
Key=value pairs to object[.[] | split("=") | {(.[0]): .[1]}] | add
jq
-rNo quotes
-sArray wrap
-nNo input
-cOne line
runningloading...
JSON Input
Output

Paste JSON and write a filter to begin.

jq reference

jq filter recipes: a copy-paste reference for every common pattern

The tool above runs jq against your JSON in the browser. The reference below is a working library of jq filters organized by category, plus the CLI flag table and the common-failure list. Every snippet is copy-paste ready for use here or in your terminal.

CLI flag reference

FlagNameWhat it doesIn Jqbin
-rRaw outputPrints strings without JSON quotesToggle "raw output"
-sSlurpWraps all input documents in an array before filteringToggle "slurp"
-cCompactPrints each result on one line, no pretty-printingToggle "compact"
-nNull inputIgnores JSON input and uses null insteadToggle "null input"
--argString varPasses a string variable to the filter: --arg name "Ada"(CLI only)
--argjsonJSON varPasses a JSON value: --argjson port 8080(CLI only)
-RRaw inputReads each line as a string instead of parsing JSON(CLI only)
-SSort keysSorts object keys in output(CLI only)
-aASCII outputEscapes non-ASCII chars as \uXXXX(CLI only)
-jJoinSuppresses newlines between outputs(CLI only)
-eExit statusExits non-zero if no output or output is null(CLI only)

Basics

Identity

.

Passes input through unchanged. The trivial filter, useful for confirming what jq sees.

Field access

.name
.user.email
.["weird key with spaces"]

Dot-notation walks an object. The square-bracket form is the escape hatch for keys that aren't valid identifiers.

Optional field access

.user.email?

Returns null instead of erroring if .user is missing or not an object.

Array indexing

.[0]
.[-1]
.[2:5]

Indexing, last element, and slicing. Slices are half-open like Python.

Array iteration

.[]

Yields each element as a separate output. The most-used operator in real jq filters.

Pipe

.users | .[0] | .name

Output of one filter becomes input to the next. Behaves like a shell pipe.

Arrays

Map a transformation across each element

map(.price * 1.1)

Equivalent to [.[] | (.price * 1.1)] but reads better.

Filter elements by predicate

map(select(.active == true))
map(select(.price >= 100))

select(cond) keeps the input when cond is truthy and drops it otherwise.

Sort

sort
sort_by(.created_at)
sort_by(.priority) | reverse

Descending sort is sort_by(...) | reverse; there is no sort_by_desc.

Group by

group_by(.category)

Returns an array of arrays. Each inner array holds elements that share the same .category value.

Count occurrences (group and tally)

group_by(.status) | map({status: .[0].status, count: length})

The canonical "group and count" idiom. Produces [{status: "open", count: 12}, ...].

Unique values

[.[] | .country] | unique
unique_by(.email)

unique sorts then dedupes. unique_by lets you specify the key.

Flatten nested arrays

flatten
flatten(1)

flatten collapses all nesting; flatten(n) collapses only n levels.

Min / max by key

min_by(.age)
max_by(.score)

Length / count

length
[.[] | select(.active)] | length

length returns array length, object key count, or string length depending on input type.

Objects

Extract specific keys

{name, email}
{name: .name, role: .user_role}

Shorthand {name} is sugar for {name: .name}.

Add or update a field

. + {processed_at: now}
.status = "shipped"

+ merges objects (right wins on conflict). = assigns in place.

Delete a field

del(.password)
del(.users[].password)

Convert object to entries (key/value pairs)

to_entries

Turns {a: 1, b: 2} into [{key: "a", value: 1}, {key: "b", value: 2}]. Required for any filter that iterates over keys.

Convert entries back to object

from_entries

Inverse of to_entries.

Filter object by key or value

with_entries(select(.value > 10))
with_entries(select(.key | startswith("user_")))

with_entries(f) is sugar for to_entries | map(f) | from_entries.

Get all keys

keys
keys_unsorted

keys returns sorted; keys_unsorted preserves insertion order.

Check for presence

has("email")
.users[0] | has("admin")

Strings and numbers

Split a string

split(",")
split(" *, *"; "g")

The second form uses a regex pattern.

Join an array of strings

join(", ")

Case conversion

ascii_downcase
ascii_upcase

Trim whitespace

gsub("^\\s+|\\s+$"; "")

jq has no built-in trim; this regex is the standard idiom.

Regex match

test("^[a-z]+$")
match("[0-9]{3}-[0-9]{4}")
capture("(?<area>[0-9]{3})-(?<line>[0-9]{4})")

test returns a boolean, match returns positions, and capture returns named groups as an object.

Substring replacement

sub("foo"; "bar")
gsub("\\bfoo\\b"; "bar")

sub replaces the first match; gsub replaces all.

String length

length

On a string, returns the number of Unicode codepoints.

Numeric formatting

. * 100 | floor
(. * 100 | round) / 100
tonumber
tostring

jq has no printf; round to N decimals by multiplying, rounding, then dividing.

Real-world patterns

Pivot: turn a list into a lookup keyed by ID

[.[] | {(.id | tostring): .}] | add

(.id | tostring) is needed because object keys must be strings.

Group, count, sort by count, take top N

group_by(.event)
  | map({event: .[0].event, count: length})
  | sort_by(-.count)
  | .[:10]

The top-10-by-frequency report. Sorting by -.count gives descending order without needing reverse.

Sum a field across all elements

[.[] | .amount] | add
map(.amount) | add

Filter out null and empty values

map(select(.email != null and .email != ""))

Conditional output

if .status == "error" then .message else empty end

empty produces no output. Use it as the "drop this element" alternative inside if.

Walk a tree and transform every value

walk(if type == "string" then ascii_downcase else . end)

walk recurses into every value in any nested structure.

Pretty-print a date field

.created_at | fromdate | strftime("%Y-%m-%d")

fromdate parses ISO 8601 strings to epoch seconds; strftime formats them.

Diff two objects (keys in A, missing from B)

(.a | keys) - (.b | keys)

Convert an array of objects to NDJSON

.[]

With -c enabled, this prints each object as one compact line, which is the NDJSON format.

Convert objects to CSV rows

.[] | [.name, .email, .signup_date] | @csv

Combined with -r, this produces lines suitable for spreadsheet import.

Output formats

FilterOutputUse when
@csvComma-separated valuesPiping to spreadsheet tools
@tsvTab-separated valuesPiping to awk or shell tools
@jsonJSON-escaped stringEmbedding a value inside another JSON string
@shShell-quoted stringBuilding shell commands safely
@uriURI percent-encoded stringBuilding URLs from JSON data
@base64Base64-encoded stringEncoding binary or text payloads
@base64dBase64-decoded stringDecoding base64 input
@htmlHTML-entity-escaped stringSafely embedding into HTML

Each is a prefix applied to an array of values:

.users | map([.name, .email]) | .[] | @csv

With -r enabled, that prints "Ada","ada@example.com" on each line.

Common gotchas

SymptomCauseFix
Cannot iterate over nullTried .[] on a missing fieldUse .field // [] | .[] to default to an empty array
Output is "string" with quotesjq prints strings as JSON by defaultToggle "raw output" (or pass -r on the CLI)
Cannot index array with string "key"Tried .key on an array instead of an objectAdd .[] or [0] first to reach into the array
group_by returns a single bucketAll elements share the same value for the keyCheck the key path; group_by(.user.id) is different from group_by(.user_id)
null returned instead of an errorOptional access ? swallowed a real bugDrop the ? temporarily and re-run to see the actual failure
Multi-document input only processes the firstDefault mode reads one JSON value at a timeToggle "slurp" to wrap everything in an array first
Filter works in one playground and fails hereVersion mismatch: gsub flags and walk arrived in 1.6Confirm with jq --version; Jqbin runs 1.8
Large integers lose precisionjq before 1.7 used IEEE 754 doublesRun on 1.7 or 1.8 for big-integer support; Jqbin is 1.8
Permalink loads but JSON is emptyCompressed payload exceeded the URL-length capShorten the JSON or share the filter only
  • JSONPath: An XPath-inspired query language for JSON. Less expressive than jq but widely supported in tools like Kubernetes kubectl and Postman. Good for read-only field extraction; weak at transformation.
  • JMESPath: AWS CLI's query language. More expressive than JSONPath, less than jq. Standardized by spec, so portable across SDKs. Comparable to jq for filtering and projection but lacks jq's full programming features.
  • NDJSON / JSON Lines: A format where each line is a standalone JSON value. jq's default input mode reads it natively; the -c flag produces it.
  • JSON Pointer (RFC 6901): A standardized notation for addressing a specific value inside a JSON document (e.g., /users/0/email). Used by JSON Patch and OpenAPI.
  • JSON Patch (RFC 6902): A format for describing changes to a JSON document. A different problem space from jq: jq queries and transforms, while JSON Patch records and applies diffs.
Read more on /learn