jq Tutorial: 20 Examples That Cover 80% of Real-World Usage
jq is the Swiss Army knife for JSON. It's a command-line tool that lets you slice, filter, map, and transform JSON data with concise expressions. If you work with APIs, log files, or any JSON data, jq will save you hours of writing throwaway Python scripts.
This tutorial teaches jq through 20 practical examples, ordered from basic to advanced. Each example uses real-world patterns you'll encounter daily. You can try every example in our jq Playground — paste the JSON, type the query, and see results instantly.
Setup
# Install jq
brew install jq # macOS
sudo apt install jq # Ubuntu/Debian
choco install jq # Windows
# Basic usage: pipe JSON into jq
echo '{"name":"Alice"}' | jq '.name'
# Output: "Alice"
curl https://api.github.com/users/octocat | jq '.'
The Basics (Examples 1-5)
1. Pretty-Print JSON
# The simplest jq command: identity filter
echo '{"a":1,"b":2,"c":3}' | jq '.'
# Output:
# {
# "a": 1,
# "b": 2,
# "c": 3
# }
The . (dot) is the identity filter — it returns the input unchanged but formatted. This alone replaces python -m json.tool for quick formatting.
2. Extract a Single Field
echo '{"name":"Alice","age":30,"email":"alice@example.com"}' | jq '.name'
# Output: "Alice"
# Nested fields
echo '{"user":{"name":"Alice","address":{"city":"Portland"}}}' | jq '.user.address.city'
# Output: "Portland"
3. Extract Multiple Fields
echo '{"name":"Alice","age":30,"email":"alice@example.com"}' | jq '.name, .age'
# Output:
# "Alice"
# 30
The comma operator produces multiple outputs. Each appears on its own line.
4. Access Array Elements
echo '[10, 20, 30, 40, 50]' | jq '.[0]' # First: 10
echo '[10, 20, 30, 40, 50]' | jq '.[-1]' # Last: 50
echo '[10, 20, 30, 40, 50]' | jq '.[1:3]' # Slice: [20, 30]
5. Iterate Over Arrays
echo '["apple","banana","cherry"]' | jq '.[]'
# Output:
# "apple"
# "banana"
# "cherry"
# Collect results back into an array with [ ]
echo '["apple","banana","cherry"]' | jq '[.[] | ascii_upcase]'
# Output: ["APPLE", "BANANA", "CHERRY"]
.[] is the array iterator — it unwraps the array into individual elements. Wrap with [...] to collect results back into an array.
Filtering and Selection (Examples 6-10)
6. Filter with select()
# Given: [{"name":"Alice","age":30}, {"name":"Bob","age":25}, {"name":"Charlie","age":35}]
jq '[.[] | select(.age > 28)]'
# Output: [{"name":"Alice","age":30}, {"name":"Charlie","age":35}]
# String matching
jq '[.[] | select(.name | startswith("A"))]'
# Output: [{"name":"Alice","age":30}]
7. Map Over Arrays
# Extract one field from each object
jq '[.[] | .name]' # Long form
jq 'map(.name)' # Short form — both produce ["Alice","Bob","Charlie"]
# Transform each element
jq 'map(.age * 2)' # Double all ages: [60, 50, 70]
jq 'map(. + {"status": "active"})' # Add a field to each object
8. Get Unique Values
# Given: [{"role":"admin"},{"role":"user"},{"role":"admin"},{"role":"user"}]
jq '[.[].role] | unique'
# Output: ["admin", "user"]
# Unique objects by a field
jq 'unique_by(.role)'
9. Sort Arrays
echo '[3, 1, 4, 1, 5, 9]' | jq 'sort'
# Output: [1, 1, 3, 4, 5, 9]
# Sort objects by field
jq 'sort_by(.age)'
# Sort descending
jq 'sort_by(.age) | reverse'
10. Count and Aggregate
# Count elements
jq 'length' # Array length
jq 'map(select(.active)) | length' # Count matching elements
# Sum numbers
jq '[.[].price] | add' # Total price
jq '[.[].score] | add / length' # Average score
Transforming Data (Examples 11-15)
11. Build New Objects
# Reshape objects
jq '.[] | {username: .name, years_old: .age}'
# Shorthand: {name} is the same as {name: .name}
jq '.[] | {name, age}'
# Dynamic keys
jq '.[] | {(.name): .age}'
# {"Alice": 30}
# {"Bob": 25}
12. Group By
# Given: users with roles
jq 'group_by(.role)'
# Output: [[admin users...], [user users...]]
# Group and count
jq 'group_by(.role) | map({role: .[0].role, count: length})'
# Output: [{"role":"admin","count":2}, {"role":"user","count":3}]
13. Flatten Nested Arrays
echo '[[1,2],[3,[4,5]],[6]]' | jq 'flatten'
# Output: [1, 2, 3, 4, 5, 6]
# Flatten one level only
echo '[[1,2],[3,[4,5]],[6]]' | jq 'flatten(1)'
# Output: [1, 2, 3, [4, 5], 6]
14. Convert Objects to Arrays and Back
# Object to key-value pairs
echo '{"a":1,"b":2}' | jq 'to_entries'
# Output: [{"key":"a","value":1}, {"key":"b","value":2}]
# Key-value pairs back to object
jq 'from_entries'
# Transform keys or values
jq 'to_entries | map(.key = "prefix_" + .key) | from_entries'
# Short form:
jq 'with_entries(.key = "prefix_" + .key)'
15. String Operations
# Split and join
echo '"hello world"' | jq 'split(" ")' # ["hello", "world"]
echo '["a","b","c"]' | jq 'join(",")' # "a,b,c"
# Case conversion
echo '"Hello World"' | jq 'ascii_downcase' # "hello world"
echo '"Hello World"' | jq 'ascii_upcase' # "HELLO WORLD"
# Regex test
echo '"foo123bar"' | jq 'test("[0-9]+")' # true
# Trim prefix/suffix
echo '"hello_world"' | jq 'ltrimstr("hello_")' # "world"
Advanced Patterns (Examples 16-20)
16. Conditional Logic
jq '.[] | if .age >= 30 then "senior" else "junior" end'
# Inline alternative operator (//)
jq '.nickname // .name // "unknown"'
# Returns first non-null/non-false value
17. Pipe Chains
# Complex transformations read left to right
jq '.users
| map(select(.active))
| sort_by(.name)
| map({name, email})'
# This reads as:
# 1. Get the users array
# 2. Keep only active users
# 3. Sort them by name
# 4. Extract just name and email
18. Working with API Responses
# GitHub API: get repo names and star counts
curl -s 'https://api.github.com/users/octocat/repos' | \
jq '[.[] | {name: .full_name, stars: .stargazers_count}] | sort_by(.stars) | reverse'
# Extract specific fields from paginated API
jq '.results | map({id, title, status})'
# Filter API errors
jq 'if .error then .error.message else .data end'
19. Get All Keys or Types
# Get all keys of an object
echo '{"a":1,"b":"two","c":true}' | jq 'keys'
# Output: ["a", "b", "c"]
# Get the type of each value
echo '{"a":1,"b":"two","c":true}' | jq 'to_entries | map({key, type: (.value | type)})'
# Output: [{"key":"a","type":"number"}, {"key":"b","type":"string"}, {"key":"c","type":"boolean"}]
20. Has and Contains
# Check if key exists
echo '{"name":"Alice","email":"alice@example.com"}' | jq 'has("email")'
# Output: true
# Check if array contains value
echo '["admin","user","editor"]' | jq 'contains(["admin"])'
# Output: true
# Filter objects that have a specific key
jq '[.[] | select(has("email"))]'
jq Cheat Sheet
| Pattern | What It Does |
|---|---|
. | Identity (pretty-print) |
.field | Get field |
.[0] | Array index |
.[] | Iterate array/object |
| | Pipe (chain operations) |
select(cond) | Filter elements |
map(f) | Transform each element |
sort_by(f) | Sort by expression |
group_by(f) | Group elements |
unique | Remove duplicates |
length | Count elements |
keys | Object keys |
add | Sum/concatenate |
to_entries | Object to key/value array |
[expr] | Collect into array |
// default | Alternative (fallback) |
Practice these patterns in our jq Playground — it runs entirely in your browser with instant results as you type.