jq - JSON Command-Line Processor

jq is a lightweight and flexible command-line JSON processor. Think of it as `sed` for JSON data.

Basic Syntax

Identity Filter

jq '.' file.json

Returns the input unchanged, but pretty-printed.

Array Length

jq 'length' file.json

Count elements in an array.

Array Access

jq '.[0]' file.json        # First element
jq '.[5]' file.json        # Sixth element (0-indexed)

Field Access

jq '.[0].name' file.json          # Get name field from first element
jq '.[0].stats.hp' file.json      # Nested field access

Array Operations

Iterator

jq '.[]' file.json                # Unwrap array, output each element
jq '.[].name' file.json           # Get name from each element

Array Slicing

jq '.[0:3]' file.json             # First 3 elements
jq '.[5:10]' file.json            # Elements 5-9

Creating Arrays

jq '[.[].name]' file.json         # Collect all names into array

Pipe Operator

Chain operations together (like Unix pipes):

jq '.[] | select(.region == "Kanto") | .name' file.json

Filtering with select()

Basic Filtering

# Find legendary Pokemon
jq '.[] | select(.legendary == true)' file.json

# Find by region
jq '.[] | select(.region == "Kanto")' file.json

# Multiple conditions
jq '.[] | select(.type | contains(["Fire"]) and .stats.speed > 80)' file.json

Collect Filtered Results

# Return as array
jq '[.[] | select(.legendary == true) | .name]' file.json

Working with Nested Data

Accessing Nested Objects

# Get data from nested results object
jq '.results[].name' file.json
jq '.results[] | {name: .name, type: .type}' file.json

Restructuring Output

# Create custom structure
jq '{results: [.results[] | {name: .name, type: .type}]}' file.json

Unique Values

Get Distinct Values

jq '[.[].region] | unique' file.json

Flatten Nested Arrays

# Get all unique types from array of type arrays
jq '[.[].type | .[]] | unique' file.json

Breaking down .[].type | .[]:

  • First .[] iterates through Pokemon
  • .type gets the type array
  • Second .[] unwraps each type array into individual strings

Grouping and Counting

Group By

# Count Pokemon by region
jq 'group_by(.region) | map({region: .[0].region, count: length})' file.json

Count by Type

# Count occurrences of each type
jq '[.[] | .type[]] | group_by(.) | map({type: .[0], count: length})' file.json

Aggregation Functions

add - Sum Array

echo '[10, 20, 30, 40]' | jq 'add'
# Output: 100

length - Count Elements

echo '[10, 20, 30, 40]' | jq 'length'
# Output: 4

Average (add / length)

echo '[10, 20, 30, 40]' | jq 'add / length'
# Output: 25

Calculate Stats by Group

# Average HP by region
jq 'group_by(.region) | map({
  region: .[0].region,
  avg_hp: (map(.stats.hp) | add / length),
  count: length
})' file.json

Sorting

# Sort by field
jq 'sort_by(.name)' file.json

# Reverse sort
jq 'sort_by(.stats.speed) | reverse' file.json

Creating Custom Objects

Basic Object Construction

jq '.[] | {name: .name, region: .region, is_legendary: .legendary}' file.json

Computed Fields

jq '.[] | {
  name: .name,
  total_hp: .stats.hp,
  is_fast: (.stats.speed > 100)
}' file.json

Conditional Logic

if-then-else

jq 'map(
  if .name == "Eevee" then
    .evolves_to = ["Vaporeon", "Jolteon", "Flareon"]
  elif .evolves_to == null then
    .evolves_to = []
  else
    .evolves_to = [.evolves_to]
  end
)' file.json

Useful Flags

-c (Compact Output)

jq -c '.[] | {name: .name, type: .type}' file.json

One JSON object per line, no pretty-printing.

-r (Raw Output)

jq -r '.results[] | "\(.name): \(.type | join(", "))"' file.json

Output raw strings without JSON quotes.

-s (Slurp)

jq -s '.' file1.json file2.json

Read multiple JSON files into a single array.

Common Patterns

Check Data Structure

jq '. | type' file.json              # Check root type (object/array)
jq 'keys' file.json                  # Get object keys
jq '.[0] | keys' file.json           # Get keys from first element

Limit Results

jq '.results[0:10]' file.json        # First 10 results
jq '.results[-5:]' file.json         # Last 5 results

Count Occurrences

jq '[.[] | select(.region == "Kanto")] | length' file.json

Find Min/Max

jq '[.[].stats.speed] | max' file.json
jq '[.[].stats.speed] | min' file.json

Practical Examples

Extract Specific Fields to New File

jq '{results: [.results[] | {name: .name, type: .type}]}' input.json > output.json

Filter and Transform

# Get all Fire-type Pokemon with speed > 80
jq '[.[] | select((.type | contains(["Fire"])) and .stats.speed > 80) |
    {name: .name, speed: .stats.speed}]' pokemon.json

Update Field Values

# Convert single value to array for all entries
jq 'map(
  if .evolves_to == null then
    .evolves_to = []
  else
    .evolves_to = [.evolves_to]
  end
)' input.json > output.json

Tips

  1. Use | liberally - Break complex queries into pipeline steps
  2. Test incrementally - Build queries step by step
  3. Wrap in [] to collect results into an array
  4. Use -c for compact output when piping to other tools
  5. Check structure first with jq 'keys' or jq '.[0]'

Resources