Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Cookbook

Common tasks and how to do them in jpx.

Extracting Data

Most JSON wrangling starts with pulling out the fields you care about.

Get a nested field

echo '{"user": {"profile": {"name": "Alice"}}}' | jpx 'user.profile.name'
# "Alice"

Get multiple fields

echo '{"id": 1, "name": "Alice", "email": "a@example.com", "age": 30}' | jpx '{id: id, name: name}'
# {"id": 1, "name": "Alice"}

Get a field from every object in an array

echo '[{"name": "Alice"}, {"name": "Bob"}]' | jpx '[*].name'
# ["Alice", "Bob"]

Get with a default value

echo '{"name": "Alice"}' | jpx 'get(@, `"email"`, `"no email"`)'
# "no email"

Filtering

Filter expressions ([?condition]) are one of JMESPath’s most powerful features. They let you select array elements that match criteria without writing loops.

Filter by a condition

echo '[{"status": "active"}, {"status": "inactive"}]' | jpx '[?status == `"active"`]'
# [{"status": "active"}]

Filter by multiple conditions

echo '[{"age": 25, "active": true}, {"age": 35, "active": true}, {"age": 30, "active": false}]' \
  | jpx '[?age > `30` && active]'
# [{"age": 35, "active": true}]

Filter by nested field

echo '[{"user": {"role": "admin"}}, {"user": {"role": "guest"}}]' | jpx '[?user.role == `"admin"`]'
# [{"user": {"role": "admin"}}]

Check if field exists

echo '[{"name": "Alice", "email": "a@b.com"}, {"name": "Bob"}]' | jpx '[?email]'
# [{"name": "Alice", "email": "a@b.com"}]

Transforming

Reshape data to match the structure you need - rename fields, compute new values, or convert between formats.

Rename fields

echo '{"firstName": "Alice", "lastName": "Smith"}' | jpx '{first: firstName, last: lastName}'
# {"first": "Alice", "last": "Smith"}

Add a computed field

echo '{"price": 100, "qty": 3}' | jpx '{price: price, qty: qty, total: multiply(price, qty)}'
# {"price": 100, "qty": 3, "total": 300}

Convert object keys to snake_case

echo '{"firstName": "Alice", "lastName": "Smith"}' | jpx 'snake_keys(@)'
# {"first_name": "Alice", "last_name": "Smith"}

Flatten nested structure

echo '{"user": {"name": "Alice", "address": {"city": "NYC"}}}' | jpx 'flatten(@)'
# {"user.name": "Alice", "user.address.city": "NYC"}

Arrays

Common operations on lists of items - sorting, deduplication, grouping, and slicing.

Sort

echo '[3, 1, 4, 1, 5]' | jpx 'sort(@)'
# [1, 1, 3, 4, 5]

Sort objects by field

echo '[{"name": "Bob", "age": 25}, {"name": "Alice", "age": 30}]' | jpx 'sort_by(@, &name)'
# [{"name": "Alice", "age": 30}, {"name": "Bob", "age": 25}]

Remove duplicates

echo '[1, 2, 2, 3, 3, 3]' | jpx 'unique(@)'
# [1, 2, 3]

First/last N items

echo '[1, 2, 3, 4, 5]' | jpx '[:3]'   # first 3
# [1, 2, 3]

echo '[1, 2, 3, 4, 5]' | jpx '[-2:]'  # last 2
# [4, 5]

Group by field

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

Chunk into batches

echo '[1, 2, 3, 4, 5, 6, 7]' | jpx 'chunk(@, `3`)'
# [[1, 2, 3], [4, 5, 6], [7]]

Aggregation

Reduce arrays to summary values - counts, sums, averages, and grouped statistics.

Count

echo '[1, 2, 3, 4, 5]' | jpx 'length(@)'
# 5

Sum, average, min, max

echo '[10, 20, 30]' | jpx 'sum(@)'
# 60

echo '[10, 20, 30]' | jpx 'avg(@)'
# 20

echo '[10, 20, 30]' | jpx 'min(@)'
# 10

Count by group

echo '[{"status": "active"}, {"status": "active"}, {"status": "inactive"}]' \
  | jpx 'group_by(@, `"status"`) | map_values(`"length(@)"`, @)'
# {"active": 2, "inactive": 1}

Strings

Text manipulation - case conversion, splitting, joining, and search/replace.

Upper/lower case

echo '"hello"' | jpx 'upper(@)'
# "HELLO"

Split and join

echo '"a,b,c"' | jpx 'split(@, `","`)'
# ["a", "b", "c"]

echo '["a", "b", "c"]' | jpx 'join(`", "`, @)'
# "a, b, c"

Replace

echo '"hello world"' | jpx 'replace(@, `"world"`, `"jpx"`)'
# "hello jpx"

Trim whitespace

echo '"  hello  "' | jpx 'trim(@)'
# "hello"

Dates

Working with timestamps - get current time, format for display, or calculate durations.

Current time

jpx -n 'now()'
# 1705312200 (Unix timestamp)

jpx -n 'format_date(now(), `"%Y-%m-%dT%H:%M:%SZ"`)'
# "2024-01-15T10:30:00Z"

Format a timestamp

jpx -n 'format_date(now(), `"%B %d, %Y"`)'
# "January 15, 2024"

# From a Unix timestamp
echo '1705312200' | jpx 'format_date(@, `"%Y-%m-%d"`)'
# "2024-01-15"

Add time to a timestamp

jpx -n 'date_add(now(), `7`, `"days"`) | format_date(@, `"%Y-%m-%d"`)'
# "2024-01-22" (7 days from now)

Working with Objects

Inspect and manipulate object structure - list keys, pick/omit fields, merge objects.

Get all keys

echo '{"a": 1, "b": 2, "c": 3}' | jpx 'keys(@)'
# ["a", "b", "c"]

Get all values

echo '{"a": 1, "b": 2, "c": 3}' | jpx 'values(@)'
# [1, 2, 3]

Pick specific keys

echo '{"a": 1, "b": 2, "c": 3}' | jpx 'pick(@, [`"a"`, `"c"`])'
# {"a": 1, "c": 3}

Omit specific keys

echo '{"a": 1, "b": 2, "c": 3}' | jpx 'omit(@, [`"b"`])'
# {"a": 1, "c": 3}

Merge objects

echo '{}' | jpx 'merge({a: `1`}, {b: `2`})'
# {"a": 1, "b": 2}

Cleaning Data

Real-world JSON is messy. These functions help you strip out nulls, empty strings, and other unwanted values.

Remove nulls

echo '{"a": 1, "b": null, "c": 3}' | jpx 'remove_nulls(@)'
# {"a": 1, "c": 3}

Remove empty strings

echo '{"a": "hello", "b": "", "c": "world"}' | jpx 'remove_empty_strings(@)'
# {"a": "hello", "c": "world"}

Compact (remove all empty values)

echo '{"a": 1, "b": null, "c": "", "d": [], "e": {}}' | jpx 'remove_empty(@)'
# {"a": 1}

Putting It Together

Chain these patterns together for real-world data processing tasks.

Extract, filter, transform, sort

echo '[
  {"name": "Alice", "dept": "eng", "salary": 100000},
  {"name": "Bob", "dept": "sales", "salary": 80000},
  {"name": "Carol", "dept": "eng", "salary": 120000}
]' | jpx '[?dept == `"eng"`] | sort_by(@, &salary) | [*].{name: name, salary: salary}'
# [{"name": "Alice", "salary": 100000}, {"name": "Carol", "salary": 120000}]

API response processing

curl -s 'https://api.github.com/users/octocat/repos' \
  | jpx '[?stargazers_count > `100`] | sort_by(@, &stargazers_count) | reverse(@) | [:5] | [*].{name: name, stars: stargazers_count}'

Log analysis

cat logs.json | jpx '[?level == `"error"`] | group_by(@, `"service"`) | map_values(`"length(@)"`, @)'
# {"api": 12, "worker": 3, "scheduler": 1}

Workflow Tips

jpx has built-in tools to help you build and reuse queries.

Explore data structure first

Before writing a query, see what you’re working with:

jpx --paths -f data.json
users (array)
users.0 (object)
users.0.name (string)
users.0.email (string)
users.0.role (string)
jpx --stats -f data.json
Type: object
Size: 2 keys
Depth: 3
Array fields:
  users: 150 items (object)
    Fields: name (100%), email (98%), role (100%), last_login (85%)

Find functions you need

Don’t memorize 400 functions - search for them:

jpx --search "date"
✓ Found 12 functions matching 'date':

  ▸ EXACT:
    date_add [datetime] - Add time to timestamp
    date_diff [datetime] - Difference between timestamps

  ▸ PREFIX:
    format_date [datetime] - Format timestamp as string
    parse_date [datetime] - Parse string to timestamp
    ...
jpx --describe format_date
format_date
===========
Category: datetime
Signature: number, string -> string
Description: Format a Unix timestamp as a string

Example:
  format_date(`1705312200`, '%Y-%m-%d') -> "2024-01-15"

Save queries to files

For complex or reusable queries, store them in a .jpx file:

-- queries.jpx

-- :name active-users
-- :description Get all active users with their email
users[?status == `"active"`].{name: name, email: email}

-- :name error-summary  
-- :description Count errors by service
[?level == `"error"`] | group_by(@, `"service"`) | map_values(`"length(@)"`, @)

Then run by name:

jpx -Q queries.jpx:active-users -f data.json

# List available queries
jpx -Q queries.jpx --list-queries
Available queries in queries.jpx:

  active-users
    Get all active users with their email

  error-summary
    Count errors by service

Debug complex expressions

When a query isn’t working, break it down:

jpx --explain 'users[?active].name | sort(@)'
Expression AST:
  Pipe
  ├─ Projection
  │  ├─ Field: users
  │  └─ Filter: active
  │     └─ Field: name
  └─ Function: sort
     └─ Current (@)

Benchmark performance

For queries you’ll run often, check how fast they are:

jpx --bench 1000 '[*].{id: id, name: upper(name)}' -f users.json
Benchmarking: [*].{id: id, name: upper(name)}
Iterations: 1000

  Mean:    0.234ms
  Median:  0.215ms
  p95:     0.312ms
  p99:     0.456ms

Throughput: 4,273 ops/sec