← Tech Guides
Command-Line Toolkit

jq

Lightweight command-line JSON processor

Like sed/awk for JSON data. Slice, filter, map, and transform structured data with zero runtime dependencies. Powerful filters meet Unix philosophy.

JSON Processing Stream Filtering Data Transformation Zero Dependencies

Quick Reference

Essential jq filters at a glance — the building blocks for JSON data transformation.

Core Filters

Identity & Access

# Pretty-print
jq '.'

# Object field access
jq '.foo'
jq '.foo.bar'

# Array access
jq '.[0]'
jq '.[-1]'     # last
jq '.[2:4]'    # slice
Iteration

Pipe & Iterate

# Pipe filters
jq '.foo | .bar'

# Multiple outputs
jq '.foo, .bar'

# Array iteration
jq '.[]'
jq '.[] | .name'
Array Operations

Map & Select

# Transform array
jq 'map(.name)'

# Filter by condition
jq 'map(select(.age > 21))'

# Sort and group
jq 'sort_by(.age)'
jq 'group_by(.dept)'
Conditionals

Logic & Defaults

# If-then-else
jq 'if .status == "ok"
  then .data
  else empty end'

# Default value
jq '.result // "not found"'

Object Construction

# Build new object from fields
jq '{name: .user, id: .uid}'

# Shorthand when key matches field
jq '{name, age}'

Basic Filters

Core filters for navigating and accessing JSON data structures.

Identity: .

The simplest filter — passes input unchanged. Essential for pretty-printing.

echo '{"name":"Alice","age":30}' | jq '.'
# {
#   "name": "Alice",
#   "age": 30
# }

Object Field Access: .foo

Access object fields using dot notation.

echo '{"name":"Alice","age":30}' | jq '.name'
# "Alice"

echo '{"user":{"name":"Bob","id":123}}' | jq '.user.name'
# "Bob"

Array Index: .[n]

Access array elements by index (0-based, negative for reverse).

echo '[10,20,30,40,50]' | jq '.[2]'
# 30

echo '[10,20,30,40,50]' | jq '.[-1]'
# 50 (last element)

Array Slice: .[start:end]

Extract array subsets (end index is exclusive).

echo '[0,1,2,3,4,5]' | jq '.[2:4]'
# [2,3]

echo '[0,1,2,3,4,5]' | jq '.[:3]'
# [0,1,2]

echo '[0,1,2,3,4,5]' | jq '.[3:]'
# [3,4,5]

Iterator: .[]

Iterate over array elements or object values.

echo '[1,2,3]' | jq '.[]'
# 1
# 2
# 3

echo '{"a":1,"b":2}' | jq '.[]'
# 1
# 2

Optional Access: .foo?

Access fields without errors if missing — returns null instead.

echo '{"name":"Alice"}' | jq '.age?'
# null

echo '[1,2,3]' | jq '.[10]?'
# null

Types & Values

Understanding and working with JSON data types in jq.

JSON Types in jq

jq supports all JSON types: null, boolean, number, string, array, object

Type Function

The type function returns the type of a value as a string.

Basic Types

echo 'null' | jq 'type'
# "null"

echo '42' | jq 'type'
# "number"

echo '"hello"' | jq 'type'
# "string"

Collections

echo '[1,2,3]' | jq 'type'
# "array"

echo '{"a":1}' | jq 'type'
# "object"

echo 'true' | jq 'type'
# "boolean"

Type Checking

# Select by type
jq 'select(type == "array")'

# Type-specific filters
jq '.[] | arrays'
jq '.[] | objects'
jq '.[] | numbers'

Null Handling

Working with Null Values

# Check for null
echo 'null' | jq '. == null'
# true

# Filter out nulls
echo '[1, null, 2, null]' | jq '[.[] | select(. != null)]'
# [1,2]

# Provide default for null
echo '{"name": null}' | jq '.name // "unknown"'
# "unknown"

Object Construction

Build new objects from existing data — reshape, extract, and transform.

Basic Object Construction

Create new objects from existing fields.

echo '{"first":"Alice","last":"Smith","age":30}' | \
  jq '{name: .first, age: .age}'
# {
#   "name": "Alice",
#   "age": 30
# }

# Shorthand when key matches field
echo '{"name":"Bob","age":25}' | jq '{name, age}'
# {
#   "name": "Bob",
#   "age": 25
# }

Computed Keys

Use parentheses for dynamic key names.

echo '{"key":"username","value":"alice"}' | \
  jq '{(.key): .value}'
# {
#   "username": "alice"
# }

# Multiple computed keys
echo '{"k1":"name","v1":"Alice","k2":"age","v2":30}' | \
  jq '{(.k1): .v1, (.k2): .v2}'
# {
#   "name": "Alice",
#   "age": 30
# }

Format Strings (String Interpolation)

Built-in formatters for encoding and transforming strings.

Format Description Example
@text Plain text (default) jq -r '.name | @text'
@json JSON encode jq '@json'
@html HTML escape jq '@html'
@uri URL encode jq '@uri'
@base64 Base64 encode jq '@base64'
@base64d Base64 decode jq '@base64d'
@sh Shell escape jq '@sh'
@csv CSV format jq '@csv'
@tsv TSV format jq '@tsv'

CSV/TSV Conversion

# Array of objects to CSV
echo '[{"name":"Alice","age":30},{"name":"Bob","age":25}]' | \
  jq -r '.[] | [.name, .age] | @csv'
# "Alice",30
# "Bob",25

# Add header row
echo '[{"name":"Alice","age":30},{"name":"Bob","age":25}]' | \
  jq -r '["name","age"], (.[] | [.name, .age]) | @csv'
# "name","age"
# "Alice",30
# "Bob",25

Array Operations

Powerful array manipulation — map, filter, sort, group, and aggregate.

map() — Transform Arrays

Apply Filter to Each Element

echo '[1,2,3,4,5]' | jq 'map(. * 2)'
# [2,4,6,8,10]

echo '[{"name":"Alice","age":30},{"name":"Bob","age":25}]' | \
  jq 'map(.name)'
# ["Alice","Bob"]

# Nested map
echo '[[1,2],[3,4]]' | jq 'map(map(. * 2))'
# [[2,4],[6,8]]

select() — Filter by Condition

Filter Array Elements

echo '[1,2,3,4,5]' | jq '[.[] | select(. > 3)]'
# [4,5]

echo '[{"name":"Alice","age":30},{"name":"Bob","age":25}]' | \
  jq '[.[] | select(.age >= 30)]'
# [{"name":"Alice","age":30}]

# Multiple conditions
echo '[1,2,3,4,5,6]' | jq '[.[] | select(. > 2 and . < 5)]'
# [3,4]

flatten — Flatten Arrays

echo '[[1,2],[3,[4,5]]]' | \
  jq 'flatten'
# [1,2,3,4,5]

# Flatten to depth
echo '[[1,[2,[3]]]]' | \
  jq 'flatten(1)'
# [1,2,[3]]

reverse — Reverse Order

echo '[1,2,3,4,5]' | \
  jq 'reverse'
# [5,4,3,2,1]

group_by() — Group Elements

Group by Expression

echo '[{"name":"Alice","dept":"eng"},{"name":"Bob","dept":"sales"},{"name":"Carol","dept":"eng"}]' | \
  jq 'group_by(.dept)'
# [
#   [{"name":"Alice","dept":"eng"},{"name":"Carol","dept":"eng"}],
#   [{"name":"Bob","dept":"sales"}]
# ]

# Group and transform
echo '[{"name":"Alice","age":30},{"name":"Bob","age":30},{"name":"Carol","age":25}]' | \
  jq 'group_by(.age) | map({age: .[0].age, people: map(.name)})'
# [
#   {"age":25,"people":["Carol"]},
#   {"age":30,"people":["Alice","Bob"]}
# ]

sort_by() — Sort Arrays

Sort by Expression

echo '[{"name":"Carol","age":25},{"name":"Alice","age":30},{"name":"Bob","age":20}]' | \
  jq 'sort_by(.age)'
# [
#   {"name":"Bob","age":20},
#   {"name":"Carol","age":25},
#   {"name":"Alice","age":30}
# ]

# Reverse sort
echo '[3,1,4,1,5]' | jq 'sort | reverse'
# [5,4,3,1,1]

# Sort by multiple fields
echo '[{"a":2,"b":1},{"a":1,"b":2},{"a":1,"b":1}]' | \
  jq 'sort_by(.a, .b)'

unique — Remove Duplicates

echo '[1,2,2,3,3,3,4]' | \
  jq 'unique'
# [1,2,3,4]

# Unique by field
jq 'unique_by(.name)'

min / max — Find Extremes

echo '[3,1,4,1,5,9,2,6]' | \
  jq 'min'
# 1

echo '[3,1,4,1,5,9,2,6]' | \
  jq 'max'
# 9

jq 'min_by(.age)'
jq 'max_by(.age)'

add — Sum Elements

echo '[1,2,3,4,5]' | \
  jq 'add'
# 15

echo '["hello"," ","world"]' | \
  jq 'add'
# "hello world"

# Default for empty
echo '[]' | jq 'add // 0'
# 0

length — Get Size

echo '[1,2,3,4,5]' | \
  jq 'length'
# 5

echo '{"a":1,"b":2,"c":3}' | \
  jq 'length'
# 3

echo '"hello"' | jq 'length'
# 5

contains / inside — Check Containment

echo '["a","b","c"]' | jq 'contains(["b"])'
# true

echo '{"a":1,"b":2}' | jq 'contains({"a":1})'
# true

echo '["b"]' | jq 'inside(["a","b","c"])'
# true

String Operations

Powerful string manipulation — split, join, match, replace, and format.

split() / join()

echo '"a,b,c,d"' | \
  jq 'split(",")'
# ["a","b","c","d"]

echo '["a","b","c"]' | \
  jq 'join(",")'
# "a,b,c"

test() — Regex Test

echo '"hello123"' | \
  jq 'test("[0-9]+")'
# true

echo '"hello"' | \
  jq 'test("^[a-z]+$")'
# true

# Case-insensitive
jq 'test("hello"; "i")'

match() — Regex Matching

Match Regex and Extract Details

echo '"The price is $42.50"' | jq 'match("\\$([0-9.]+)")'
# {
#   "offset": 13,
#   "length": 6,
#   "string": "$42.50",
#   "captures": [
#     {
#       "offset": 14,
#       "length": 5,
#       "string": "42.50",
#       "name": null
#     }
#   ]
# }

# Global match
echo '"abc123def456"' | jq '[match("[0-9]+"; "g")]'
# [
#   {"offset":3,"length":3,"string":"123","captures":[]},
#   {"offset":9,"length":3,"string":"456","captures":[]}
# ]

capture() — Named Groups

Extract Named Groups from Regex

echo '"User: alice, ID: 123"' | \
  jq 'capture("User: (?\\w+), ID: (?\\d+)")'
# {
#   "name": "alice",
#   "id": "123"
# }

# Use captured values
echo '"price: $42.50"' | \
  jq 'capture("\\$(?[0-9.]+)") | .price | tonumber'
# 42.5

sub() / gsub() — String Replacement

Substitute Strings

# Replace first occurrence
echo '"hello world"' | jq 'sub("l"; "L")'
# "heLlo world"

# Replace all occurrences
echo '"hello world"' | jq 'gsub("l"; "L")'
# "heLLo worLd"

# Regex replacement
echo '"price: $42.50"' | jq 'gsub("\\$[0-9.]+"; "REDACTED")'
# "price: REDACTED"

# Backreferences
echo '"2024-01-15"' | \
  jq 'gsub("(?[0-9]{4})-(?[0-9]{2})-(?[0-9]{2})"; "\\(.d)/\\(.m)/\\(.y)")'
# "15/01/2024"

ltrimstr / rtrimstr

# Remove prefix
jq 'ltrimstr("https://")'

# Remove suffix
jq 'rtrimstr(".txt")'

Case Conversion

jq 'ascii_downcase'
# "hello world"

jq 'ascii_upcase'
# "HELLO WORLD"

startswith / endswith

jq 'startswith("hello")'
# true

jq 'endswith(".pdf")'
# true

String Interpolation

Use \(expr) Inside Strings

echo '{"name":"Alice","age":30}' | \
  jq '"Hello, \(.name)! You are \(.age) years old."'
# "Hello, Alice! You are 30 years old."

# With expressions
echo '{"price":42.5}' | \
  jq '"Total: $\(.price * 1.1 | floor)"'
# "Total: $46"

tonumber / tostring

echo '"42"' | jq 'tonumber'
# 42

echo '42' | jq 'tostring'
# "42"

jq 'tonumber | . * 2'

indices() — Find All

echo '["a","b","c","b","d"]' | \
  jq 'indices("b")'
# [1,3]

echo '[1,2,3,2,1]' | \
  jq 'indices(2)'
# [1,3]

Conditionals & Comparisons

Logic, conditionals, and control flow for dynamic transformations.

if-then-else

Basic Conditional Logic

echo '{"age":25}' | \
  jq 'if .age >= 18 then "adult" else "minor" end'
# "adult"

# Multiple conditions with elif
echo '42' | \
  jq 'if . < 33 then "small" elif . < 66 then "medium" else "large" end'
# "medium"

# Nested conditionals
echo '{"status":"success","data":123}' | \
  jq 'if .status == "success" then
    (if .data then .data else 0 end)
  else null end'
# 123

Alternative Operator: //

Provide Default Value for Null/False

echo '{"name":"Alice"}' | jq '.age // 0'
# 0

echo 'null' | jq '. // "default"'
# "default"

echo 'false' | jq '. // "default"'
# "default"

echo '[]' | jq 'add // 0'
# 0

# Chain alternatives
echo '{}' | jq '.a // .b // .c // "none"'
# "none"

try-catch

Error Handling

# Handle errors gracefully
echo '{"name":"Alice"}' | jq 'try .age catch "not found"'
# "not found"

# Suppress errors with ?
echo '{"name":"Alice"}' | jq '.age?'
# null

echo '"not-a-number"' | jq 'try tonumber catch 0'
# 0

Comparison Operators

Operator Description Example
== Equal to .a == .b
!= Not equal to .a != .b
> Greater than .x > 5
>= Greater than or equal .x >= 10
< Less than .x < 20
<= Less than or equal .x <= 10

Logical Operators

# and
echo '{"x":10,"y":20}' | jq '.x > 5 and .y < 30'
# true

# or
echo '{"status":"error"}' | jq '.status == "error" or .status == "warning"'
# true

# not
echo '{"active":true}' | jq '.active | not'
# false

# Complex logic
echo '{"age":25,"verified":true}' | jq '.age >= 18 and .verified'
# true
Truthiness in jq

Only false and null are falsy — everything else is truthy. This means 0, "", and [] are all truthy values.

Advanced Filters

Power tools for complex transformations — reduce, recurse, walk, and more.

reduce — Iteration with Accumulator

Sum Array

echo '[1,2,3,4,5]' | \
  jq 'reduce .[] as $item (0; . + $item)'
# 15

Build Object from Array

echo '[{"name":"Alice","age":30},{"name":"Bob","age":25}]' | \
  jq 'reduce .[] as $item ({}; .[$item.name] = $item.age)'
# {
#   "Alice": 30,
#   "Bob": 25
# }

Count Occurrences

echo '["a","b","a","c","b","a"]' | \
  jq 'reduce .[] as $item ({}; .[$item] = (.[$item] // 0) + 1)'
# {
#   "a": 3,
#   "b": 2,
#   "c": 1
# }

recurse — Recursive Application

Traverse Nested Structure

# Generate sequence
echo '1' | jq '[limit(5; recurse(. + 1))]'
# [1,2,3,4,5]

# Traverse nested structure
echo '{"name":"root","children":[{"name":"a","children":[{"name":"b"}]},{"name":"c"}]}' | \
  jq '.. | objects | .name'
# "root"
# "a"
# "b"
# "c"

walk() — Recursive Transformation

Apply Filter to All Values

# Increment all numbers
echo '{"a":1,"b":{"c":2,"d":3}}' | \
  jq 'walk(if type == "number" then . + 1 else . end)'
# {
#   "a": 2,
#   "b": {
#     "c": 3,
#     "d": 4
#   }
# }

# Remove null values recursively
echo '{"a":1,"b":null,"c":{"d":2,"e":null}}' | \
  jq 'walk(if type == "object" then with_entries(select(.value != null)) else . end)'
# {
#   "a": 1,
#   "c": {
#     "d": 2
#   }
# }

limit() / first() / last()

# Limit outputs
jq '[limit(3; .[])]'
# [1,2,3]

# First element
jq 'first'
# 1

# Last element
jq 'last'
# 5

# Nth element
jq 'nth(2)'
# 30

until() — Repeat

# Find next power of 2
echo '50' | \
  jq 'until(. >= 50; . * 2)'
# 64

# Countdown
echo '5' | \
  jq '[., until(. <= 0; . - 1)]'
# [5,4,3,2,1,0]

env — Environment Variables

# Get environment variable
USER=alice jq -n 'env.USER'
# "alice"

# Use in filter
PORT=8080 jq -n '{
  host: "localhost",
  port: (env.PORT | tonumber)
}'

input / inputs

# Read next input
jq -n 'input'

# Read all inputs
jq -n '[inputs]'

# Combine inputs
jq -s 'add'

Combining Filters

Master filter composition — pipe, comma, and control flow operators.

Pipe: |

Feed Output to Next Filter

echo '{"user":{"name":"Alice","age":30}}' | jq '.user | .name'
# "Alice"

# Chain multiple operations
echo '[3,1,4,1,5,9]' | jq 'sort | reverse | .[0:3]'
# [9,5,4]

Comma: ,

Produce Multiple Outputs

echo '{"name":"Alice","age":30}' | jq '.name, .age'
# "Alice"
# 30

# Collect with array
echo '{"name":"Alice","age":30}' | jq '[.name, .age]'
# ["Alice",30]

# Multiple transformations
echo '{"x":10}' | jq '.x, .x * 2, .x * 3'
# 10
# 20
# 30

Parentheses — Grouping

echo '[1,2,3]' | jq '(.[] | . * 2)'
# 2
# 4
# 6

# Vs without parentheses
echo '[1,2,3]' | jq '[.[] | . * 2]'
# [2,4,6]
Semicolon in Function Parameters

The semicolon ; is used to separate function parameters, not for sequencing operations.

# limit(n; expr)
jq '[limit(5; range(10))]'

# until(condition; update)
jq 'until(. > 100; . * 2)'

Practical Patterns

Real-world use cases and common workflows for JSON data processing.

API Response Parsing

Extract & Transform API Data

# Extract specific fields from GitHub API
curl -s https://api.github.com/repos/jqlang/jq | \
  jq '{name, stars: .stargazers_count, issues: .open_issues_count}'

# Get list of user repos
curl -s https://api.github.com/users/octocat/repos | \
  jq '[.[] | {name, url: .html_url, stars: .stargazers_count}]'

# Filter and sort
curl -s https://api.github.com/users/octocat/repos | \
  jq '[.[] | select(.stargazers_count > 10)] | sort_by(-.stargazers_count) | .[0:5]'

Log File Analysis

Parse JSON Logs

# Filter errors
cat app.log | \
  jq 'select(.level == "error")'

# Count by type
cat app.log | \
  jq -s 'group_by(.error_type) |
  map({
    type: .[0].error_type,
    count: length
  })'

Extract Time Range

cat app.log | \
  jq 'select(
    .timestamp >= "2024-01-01"
    and
    .timestamp < "2024-02-01"
  )'

Aggregate Statistics

cat metrics.log | jq -s '{
  total: length,
  avg_duration: (
    map(.duration) | add / length
  ),
  max_duration: (
    map(.duration) | max
  ),
  min_duration: (
    map(.duration) | min
  )
}'

Config File Transformation

Update Config

# Update config value
jq '.database.port = 5432' \
  config.json > config.new.json

# Merge configs
jq -s '.[0] * .[1]' \
  config1.json config2.json \
  > merged.json

Extract & Clean

# Remove sensitive data
jq 'del(.secrets, .api_keys)' \
  config.json

# Extract environment
jq '.environments.production' \
  config.json

Data Reshaping

Pivot: Array to Object

echo '[{"id":"a","value":1},{"id":"b","value":2}]' | \
  jq 'map({(.id): .value}) | add'
# {"a":1,"b":2}

Unpivot: Object to Array

echo '{"a":1,"b":2,"c":3}' | \
  jq 'to_entries | map({key, value})'
# [
#   {"key":"a","value":1},
#   {"key":"b","value":2},
#   {"key":"c","value":3}
# ]

Flatten Nested Structure

echo '{"users":[{"name":"Alice","addresses":[{"city":"NYC"},{"city":"LA"}]}]}' | \
  jq '[.users[] | {name, city: .addresses[].city}]'
# [
#   {"name":"Alice","city":"NYC"},
#   {"name":"Alice","city":"LA"}
# ]

Group & Aggregate

echo '[{"dept":"eng","salary":100},{"dept":"sales","salary":80},{"dept":"eng","salary":120}]' | \
  jq 'group_by(.dept) |
  map({
    dept: .[0].dept,
    total: (map(.salary) | add),
    count: length
  })'
# [
#   {"dept":"eng","total":220,"count":2},
#   {"dept":"sales","total":80,"count":1}
# ]

Validation & Filtering

Validate Required Fields

jq 'select(.name and .email and .age)' \
  users.json

# Remove invalid entries
jq '[.[] | select(.age >= 0 and .age <= 150)]' \
  users.json

Check Data Quality

# Validate email format
jq '[.[] | select(.email | test("@.*\\..*"))]' \
  users.json

# Find inconsistencies
jq '[.[] | select(.created_at > .updated_at)]' \
  records.json

Pro Tips

Expert workflows, performance optimization, and advanced techniques.

jq with curl

# Pretty-print API response
curl -s https://api.github.com/users/octocat | jq '.'

# Extract single field
curl -s https://api.github.com/users/octocat | jq -r '.name'

# Store token, use in request
TOKEN=$(jq -r '.token' auth.json)
curl -H "Authorization: Bearer $TOKEN" https://api.example.com/data | jq '.'

# Pipe to other commands
curl -s https://api.github.com/users/octocat/repos | \
  jq -r '.[].clone_url' | xargs -I {} git clone {}

jq in Shell Scripts

#!/bin/bash
# Read config
DB_HOST=$(jq -r '.database.host' config.json)
DB_PORT=$(jq -r '.database.port' config.json)

# Process array
jq -c '.[]' data.json | while read -r item; do
  name=$(echo "$item" | jq -r '.name')
  echo "Processing: $name"
done

# Build JSON
output=$(jq -n \
  --arg host "$HOSTNAME" \
  --arg time "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
  '{hostname: $host, timestamp: $time, status: "ok"}')

# Validate JSON
if jq empty file.json 2>/dev/null; then
  echo "Valid JSON"
else
  echo "Invalid JSON"
fi

Command-Line Options

Option Description Example
-r Raw output (no JSON quotes) jq -r '.name'
-c Compact output (no pretty-print) jq -c '.'
-s Slurp (read entire input into array) jq -s 'add'
-n Null input (don't read input) jq -n '{hello: "world"}'
-e Exit status based on output jq -e '.error'
-f Read filter from file jq -f filter.jq data.json
--arg Pass string variable jq --arg name "Alice" '{name: $name}'
--argjson Pass JSON variable jq --argjson age 30 '{age: $age}'
--slurpfile Read JSON file into variable jq --slurpfile cfg config.json
--stream Parse in streaming mode (large files) jq --stream '.' large.json

Performance Tips

Optimize Processing

# Use --stream for large files
jq --stream 'select(.[0][0] == "target")' \
  huge.json

# Use limit() to stop early
jq 'limit(10; .[] | select(.age > 30))' \
  users.json

# Single pass vs multiple
# BAD: Multiple passes
jq '.[] | select(.age > 30)' | \
  jq 'map(.name)'

# GOOD: Single pass
jq '[.[] | select(.age > 30) | .name]'

Efficient Output

# Compact output (faster, less bandwidth)
jq -c '.' data.json

# Read multiple files efficiently
jq -s 'add' \
  file1.json \
  file2.json \
  file3.json

# Use --tab for TSV
jq -r '.[] | [.name, .age] | @tsv' \
  users.json

Debugging Techniques

# Print intermediate values with debug
echo '{"a":1,"b":2}' | jq '.a | debug | . * 2'
# ["DEBUG:",1]
# 2

# Use stderr for logging
echo '{"a":1}' | jq '.a | debug("value is:") | . * 2'

# Check types
echo '{"data":"123"}' | jq '.data | type, tonumber | type'
# "string"
# "number"

# Use $__loc__ for location info
echo '{"a":{"b":1}}' | jq '.a.b | $__loc__'

Common Gotchas

Watch Out For These Common Mistakes
  • Strings vs raw strings: jq '.name' outputs "Alice" (JSON string), use -r for raw output
  • Empty vs null: Only false and null are falsy — "", 0, and [] are truthy
  • Array wrapping: .[] produces multiple outputs, use [.[]] to wrap in array
  • Computed keys: Use {(.key): "value"} not {.key: "value"}
  • Regex escaping: Must escape backslashes: test("\\d+")
  • Integer division: 5 / 2 returns 2.5, use floor for integers
  • Null propagation: Use .a?.b?.c? to safely traverse nullable paths

Alternative Tools

yq

YAML Processor

jq for YAML — same syntax, different format.

yq eval '.servers[0].host' \
  config.yaml

# Convert YAML to JSON
yq eval -o=json '.' \
  config.yaml | jq '.'

gojq

Go Implementation

Pure Go jq — faster startup, library usage.

gojq '.' data.json

# Same syntax as jq
# Can be used as library

jaq

Rust Clone

Rust-based jq — significantly faster for large files.

jaq '.' data.json

# Compatible with most jq
# Faster for large datasets

fx

Terminal Viewer

Interactive JSON viewer with autocomplete.

fx data.json

# Interactive browsing
# JavaScript-based filtering

jless

Interactive Viewer

Vim-like JSON viewer with tree expansion.

jless data.json

# Vim keybindings
# Expandable tree view

Online Playground

Test jq Filters Online

Use jqplay.org — the official interactive playground for testing jq filters with real-time results, shareable URLs, and syntax highlighting.

Alternative: DevToolsDaily jq Playground

Installation

Package Managers

# macOS
brew install jq

# Ubuntu/Debian
sudo apt-get install jq

# Fedora
sudo dnf install jq

# Windows (Chocolatey)
choco install jq

From Source

git clone \
  https://github.com/jqlang/jq.git
cd jq
autoreconf -fi
./configure
make
sudo make install