Introduction
This documentation covers the JMESPath Extensions project, which provides 400+ additional functions for JMESPath queries.
Project Components
This project includes multiple components:
| Component | Description | Documentation |
|---|---|---|
| jmespath_extensions | Rust library with 400+ extension functions | docs.rs |
| jpx | Command-line tool for querying JSON | This site |
| jmespath-extensions-py | Python bindings | Python section |
| MCP Server | AI assistant integration | MCP section |
This documentation primarily focuses on jpx, the CLI tool. For using the Rust library directly in your code, see the rustdoc documentation.
What is JMESPath?
JMESPath is a query language for JSON. It allows you to declaratively specify how to extract and transform elements from a JSON document.
Why JMESPath Extensions?
While standard JMESPath is powerful, it’s intentionally minimal with only 26 built-in functions. This project extends JMESPath with 400+ additional functions for:
- String manipulation:
upper,lower,split,replace,camel_case,snake_case, and more - Array operations:
unique,chunk,flatten,group_by,zip, and more - Object manipulation:
pick,omit,deep_merge,flatten_keys, and more - Math & statistics:
round,sqrt,median,stddev,percentile, and more - Date/time:
now,format_date,parse_date,date_add,date_diff - Hashing & encoding:
md5,sha256,base64_encode,hex_encode - Fuzzy matching:
levenshtein,jaro_winkler,soundex - And much more: geo functions, validation, regex, UUIDs, network utilities…
Features
- Rust Library: Use the functions in your Rust applications (docs.rs)
- Powerful CLI: Query JSON from files, stdin, or inline with jpx
- MCP Server: Use jpx with AI assistants like Claude
- Python Bindings: Use the same 400+ functions in Python
- Function Discovery: Built-in help for all functions
- Strict Mode: Use only standard JMESPath for portable queries
Quick Example
# Basic query
echo '{"users": [{"name": "alice"}, {"name": "bob"}]}' | jpx 'users[*].name'
# ["alice", "bob"]
# Using extension functions
echo '{"name": "hello world"}' | jpx 'upper(name)'
# "HELLO WORLD"
echo '{"items": [1, 2, 2, 3, 3, 3]}' | jpx 'unique(items)'
# [1, 2, 3]
echo '{"values": [10, 20, 30, 40, 50]}' | jpx 'median(values)'
# 30
Getting Started
Ready to dive in? Start with Installation to get jpx set up on your system.
Installation
Docker (Quickest)
Try jpx instantly without installing anything:
# Pull the image
docker pull ghcr.io/joshrotenberg/jpx
# Run a query
echo '{"name": "Alice", "age": 30}' | docker run -i ghcr.io/joshrotenberg/jpx 'name'
# "Alice"
# Fetch and transform data from an API
curl -s 'https://hacker-news.firebaseio.com/v0/item/1.json' | \
docker run -i ghcr.io/joshrotenberg/jpx '{title: title, by: by, score: score}'
# {"by": "pg", "score": 57, "title": "Y Combinator"}
Available for linux/amd64 and linux/arm64.
Homebrew (macOS/Linux)
The easiest way to install jpx on macOS or Linux:
brew tap joshrotenberg/brew
brew install jpx
Pre-built Binaries
Download pre-built binaries for your platform from the GitHub Releases page.
Available platforms:
- macOS (Apple Silicon / arm64)
- macOS (Intel / x86_64)
- Linux (x86_64)
- Windows (x86_64)
Cargo (from crates.io)
If you have Rust installed:
cargo install jpx
From Source
Clone and build from source:
git clone https://github.com/joshrotenberg/jmespath-extensions
cd jmespath-extensions/jpx
cargo install --path .
Without MCP Server Support
MCP support is included by default. To build without it (smaller binary):
cargo install --path . --no-default-features
Verify Installation
Check that jpx is installed correctly:
jpx --version
You should see output like:
jpx 0.1.15
Shell Completions
Generate shell completions for your shell:
# Bash
jpx --completions bash > ~/.local/share/bash-completion/completions/jpx
# Zsh
jpx --completions zsh > ~/.zfunc/_jpx
# Fish
jpx --completions fish > ~/.config/fish/completions/jpx.fish
# PowerShell
jpx --completions powershell > jpx.ps1
Quick Start
jpx is a command-line JSON processor with 400+ built-in functions. Pipe JSON in, get transformed JSON out.
Five things to know to start using it:
1. Get a field
echo '{"name": "Alice", "city": "NYC"}' | jpx 'name'
# "Alice"
Nested fields use dots:
echo '{"user": {"name": "Alice"}}' | jpx 'user.name'
# "Alice"
2. Get all items from an array
Use [*] to get every element:
echo '[{"name": "Alice"}, {"name": "Bob"}]' | jpx '[*].name'
# ["Alice", "Bob"]
3. Filter an array
Use [?condition] to filter:
echo '[{"name": "Alice", "age": 30}, {"name": "Bob", "age": 25}]' | jpx '[?age > `28`]'
# [{"name": "Alice", "age": 30}]
Note: literal values use backticks (`28`).
4. Call a function
jpx has 400+ functions. Pass data with @ (current value):
echo '[3, 1, 4, 1, 5]' | jpx 'sort(@)'
# [1, 1, 3, 4, 5]
echo '[3, 1, 4, 1, 5]' | jpx 'unique(@)'
# [3, 1, 4, 5]
echo '{"name": "hello"}' | jpx 'upper(name)'
# "HELLO"
5. Chain with pipes
Combine operations with |:
echo '[{"n": "Alice"}, {"n": "Bob"}, {"n": "Alice"}]' | jpx '[*].n | unique(@) | sort(@)'
# ["Alice", "Bob"]
That’s it. You can do a lot with just these five patterns.
Finding Functions
Don’t memorize 400 functions. Search for what you need:
jpx --search unique
jpx --describe unique
Next Steps
- Cookbook - Common tasks and recipes
- Basic Usage - More CLI options
- Function Reference - All functions by category
- Why jpx? - Compare jpx to Python, jq, and other tools
Basic Usage
Input Sources
jpx can read JSON from multiple sources:
Standard Input (stdin)
echo '{"name": "Alice"}' | jpx 'name'
# "Alice"
cat data.json | jpx 'users[*].name'
curl -s https://api.example.com/data | jpx 'results[0]'
File Input
jpx 'users[*].name' -f data.json
jpx --file users.json 'length(@)'
Null Input
Use -n to start with null (useful for functions that don’t need input):
jpx -n 'now()'
# 1705312200
jpx -n 'uuid()'
# "550e8400-e29b-41d4-a716-446655440000"
jpx -n 'range(`1`, `5`)'
# [1, 2, 3, 4]
Output Options
Pretty Printing (default)
By default, jpx pretty-prints JSON output with colors:
echo '{"a":1,"b":2}' | jpx '@'
Output:
{
"a": 1,
"b": 2
}
Compact Output
Use -c for single-line output:
echo '{"a":1,"b":2}' | jpx -c '@'
Output:
{"a":1,"b":2}
Raw Strings
Use -r to output strings without quotes:
echo '{"msg": "hello"}' | jpx -r 'msg'
Output:
hello
Output to File
Write results to a file:
jpx 'users[*].email' -f data.json -o emails.json
# (writes to emails.json)
Slurp Mode
Read multiple JSON objects into an array:
echo '{"a":1}
{"b":2}
{"c":3}' | jpx -s 'length(@)'
Output:
3
Expression as Positional Argument
The expression can be the first positional argument:
jpx 'users[*].name' -f data.json
Or use -e / --expression:
jpx -e 'users[*].name' -f data.json
Chaining Expressions
Three ways to chain transformations:
# 1. Pipes within a single expression (most common)
jpx 'users | [*].name | sort(@)' -f data.json
# 2. Multiple positional arguments
jpx 'users' '[*].name' 'sort(@)' -f data.json
# 3. Multiple -e flags
jpx -e 'users' -e '[*].name' -e 'sort(@)' -f data.json
All three are equivalent - each expression receives the output of the previous one.
Verbose Mode
See expression details and timing:
echo '{"x": 1}' | jpx -v 'x'
Output:
Input: object (1 keys)
[1] Expression: x
[1] Result: number (1)
[1] Time: 0.040ms
Total time: 0.214ms
1
Quiet Mode
Suppress errors and warnings:
jpx -q 'invalid[' -f data.json
# (no error output, exits with non-zero status)
Color Control
Control colorized output:
jpx --color=always 'name' -f data.json # Force colors
jpx --color=never 'name' -f data.json # No colors
jpx --color=auto 'name' -f data.json # Auto-detect (default)
CLI Reference
Complete reference for all jpx command-line options.
Synopsis
jpx [OPTIONS] [EXPRESSIONS]...
Arguments
| Argument | Description |
|---|---|
[EXPRESSIONS]... | JMESPath expression(s) to evaluate (multiple are chained as a pipeline) |
Options
Input/Output
| Option | Description |
|---|---|
-e, --expression <EXPR> | Expression(s) to evaluate (can be repeated) |
-Q, --query-file <FILE> | Read JMESPath expression from file (supports .jpx libraries with colon syntax: file.jpx:query-name) |
--query <NAME> | Select a named query from a .jpx library |
--list-queries | List all queries in a .jpx library file |
--check | Validate all queries in a .jpx library without running |
-f, --file <FILE> | Input JSON file (reads stdin if not provided) |
-o, --output <FILE> | Output file (writes to stdout if not provided) |
-n, --null-input | Don’t read input, use null as input value |
-s, --slurp | Read all inputs into an array |
--stream | Stream mode - process input line by line (for NDJSON/JSON Lines) |
Output Format
| Option | Description |
|---|---|
-r, --raw | Output raw strings without quotes |
-c, --compact | Compact output (no pretty printing) |
-y, --yaml | Output as YAML |
--toml | Output as TOML |
--csv | Output as CSV (comma-separated values) |
--tsv | Output as TSV (tab-separated values) |
-l, --lines | Output one JSON value per line |
-t, --table | Output as a formatted table (for arrays of objects) |
--table-style <STYLE> | Table style: unicode (default), ascii, markdown, plain |
--color <MODE> | Colorize output: auto, always, never |
See Output Formats for detailed examples.
Modes
| Option | Description |
|---|---|
--strict | Strict mode - only standard JMESPath functions |
-v, --verbose | Show expression details and timing |
-q, --quiet | Suppress errors and warnings |
JSON Patch Operations
| Option | Description |
|---|---|
--diff <SOURCE> <TARGET> | Generate JSON Patch (RFC 6902) from two files |
--patch <PATCH_FILE> | Apply JSON Patch (RFC 6902) to input |
--merge <MERGE_FILE> | Apply JSON Merge Patch (RFC 7396) to input |
Data Analysis
| Option | Description |
|---|---|
--stats | Show statistics about the input data |
--paths | List all paths in the input JSON |
--types | Show types alongside paths (use with --paths) |
--values | Show values alongside paths (use with --paths) |
Benchmarking
| Option | Description |
|---|---|
--bench [N] | Benchmark expression performance (default: 100 iterations) |
--warmup <N> | Number of warmup iterations before benchmarking (default: 5) |
Function Discovery
| Option | Description |
|---|---|
--list-functions | List all available functions |
--list-category <NAME> | List functions in a specific category |
--describe <FUNCTION> | Show detailed info for a function |
--search <QUERY> | Search functions by name, description, or category (fuzzy matching) |
--similar <FUNCTION> | Find functions similar to the specified function |
Debugging
| Option | Description |
|---|---|
--explain | Show how an expression is parsed (AST) |
--debug | Show diagnostic information for troubleshooting |
Interactive Mode
| Option | Description |
|---|---|
--repl | Start interactive REPL mode |
--demo <NAME> | Load a demo dataset (use with --repl) |
Other
| Option | Description |
|---|---|
--completions <SHELL> | Generate shell completions (bash, zsh, fish, powershell, elvish) |
-h, --help | Print help |
-V, --version | Print version |
Environment Variables
| Variable | Description |
|---|---|
JPX_VERBOSE=1 | Enable verbose mode |
JPX_QUIET=1 | Enable quiet mode |
JPX_STRICT=1 | Enable strict mode |
JPX_RAW=1 | Output raw strings |
JPX_COMPACT=1 | Compact output |
Environment variables are overridden by command-line flags.
Quick Examples
A few examples showing common flag combinations. For comprehensive examples, see:
- Basic Usage - getting started with queries
- Cookbook - task-oriented recipes
- Output Formats - tables, CSV, YAML
- Query Files - reusable query libraries
# Query a file, raw output for scripting
jpx -r 'config.api_key' -f settings.json
# Chain expressions, table output
jpx 'users' '[?active]' -t -f data.json
# Stream NDJSON, filter errors
cat logs.ndjson | jpx --stream '[?level == `"error"`]'
# Analyze structure before querying
jpx --paths --types -f data.json
# Find functions by keyword
jpx --search unique
# Run a named query from a library
jpx -Q queries.jpx:active-users -f data.json
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Error (invalid expression, file not found, etc.) |
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
Output Formats
jpx supports multiple output formats beyond JSON, making it easy to integrate with other tools and workflows.
Available Formats
| Flag | Short | Description |
|---|---|---|
--yaml | -y | YAML format |
--toml | TOML format | |
--csv | Comma-separated values | |
--tsv | Tab-separated values | |
--lines | -l | One JSON value per line (JSONL) |
--table | -t | Formatted table (for arrays of objects) |
YAML Output
YAML is great for human-readable configuration files and nested data structures.
# Simple object
echo '{"name": "alice", "age": 30}' | jpx '@' --yaml
age: 30
name: alice
# Nested structures
echo '{"server": {"host": "localhost", "port": 8080}}' | jpx '@' -y
server:
host: localhost
port: 8080
# Arrays
echo '[{"name": "alice"}, {"name": "bob"}]' | jpx '@' --yaml
- name: alice
- name: bob
Use Cases
- Converting JSON configs to YAML for Kubernetes, Docker Compose, etc.
- Human-readable output for debugging
- Generating configuration files
# Extract and convert to YAML
jpx '[*].{name: name, role: role}' -f users.json --yaml > users.yaml
TOML Output
TOML is ideal for configuration files, especially in the Rust ecosystem.
# Simple object
echo '{"name": "myapp", "version": "1.0.0"}' | jpx '@' --toml
name = "myapp"
version = "1.0.0"
# Nested tables
echo '{"database": {"host": "localhost", "port": 5432}}' | jpx '@' --toml
[database]
host = "localhost"
port = 5432
Use Cases
- Generating Cargo.toml snippets
- Creating configuration files for Rust applications
- Converting JSON API responses to TOML configs
CSV Output
CSV format is perfect for tabular data that needs to go into spreadsheets or databases.
# Array of objects becomes a table
echo '[{"name": "alice", "age": 30}, {"name": "bob", "age": 25}]' | jpx '@' --csv
age,name
30,alice
25,bob
Nested Object Flattening
Nested objects are automatically flattened using dot notation:
echo '[
{"name": "alice", "address": {"city": "NYC", "zip": "10001"}},
{"name": "bob", "address": {"city": "LA", "zip": "90001"}}
]' | jpx '@' --csv
address.city,address.zip,name
NYC,10001,alice
LA,90001,bob
Arrays in Cells
Arrays are JSON-encoded within cells:
echo '[{"name": "alice", "tags": ["admin", "dev"]}]' | jpx '@' --csv
name,tags
alice,"[""admin"",""dev""]"
Single Objects
A single object is output as a single-row table:
echo '{"name": "alice", "role": "admin"}' | jpx '@' --csv
name,role
alice,admin
Array of Primitives
Arrays of primitives become single-column output (no header):
echo '[1, 2, 3, 4, 5]' | jpx '@' --csv
1
2
3
4
5
Use Cases
- Exporting data for Excel or Google Sheets
- Loading into databases
- Generating reports
# Export user emails for a mailing list
jpx '[*].{email: email, name: name}' -f users.json --csv > contacts.csv
TSV Output
TSV (tab-separated values) works the same as CSV but uses tabs instead of commas. This is useful for:
- Unix command-line tools that expect tab-separated input
- Pasting directly into spreadsheets
- Avoiding issues with commas in data
echo '[{"name": "alice", "city": "New York"}, {"name": "bob", "city": "Los Angeles"}]' | jpx '@' --tsv
city name
New York alice
Los Angeles bob
Use Cases
# Pipe to awk for further processing
jpx '[*].{name: name, score: score}' -f data.json --tsv | awk -F'\t' '$2 > 90'
# Pipe to column for pretty printing
jpx '@' -f users.json --tsv | column -t -s $'\t'
Lines Output (JSONL)
The --lines flag outputs one JSON value per line, also known as JSON Lines (JSONL) or newline-delimited JSON (NDJSON).
echo '[1, 2, 3, "hello", {"key": "value"}]' | jpx '@' --lines
1
2
3
"hello"
{"key":"value"}
# Array of objects
echo '[{"id": 1}, {"id": 2}, {"id": 3}]' | jpx '@' -l
{"id":1}
{"id":2}
{"id":3}
Use Cases
- Streaming processing with tools like
jq,grep, orawk - Log file format
- Parallel processing (each line is independent)
- Piping to other JSON tools
# Process each line with another tool
jpx '[*]' -f events.json --lines | while read -r line; do
echo "$line" | jpx '.timestamp'
done
# Filter with grep
jpx '@' -f logs.json --lines | grep '"level":"error"'
# Count items
jpx '[*]' -f data.json --lines | wc -l
Table Output
The --table flag renders arrays of objects as formatted tables, perfect for terminal display.
echo '[{"name": "alice", "age": 30}, {"name": "bob", "age": 25}]' | jpx '@' --table
┌───────┬─────┐
│ name │ age │
├───────┼─────┤
│ alice │ 30 │
│ bob │ 25 │
└───────┴─────┘
Table Styles
Use --table-style to choose different table formats:
| Style | Description |
|---|---|
unicode | Default, uses box-drawing characters |
ascii | ASCII characters only (+, -, ` |
markdown | GitHub-flavored markdown tables |
plain | No borders, space-separated |
# ASCII style (for older terminals)
echo '[{"name": "alice", "age": 30}]' | jpx '@' -t --table-style ascii
+-------+-----+
| name | age |
+-------+-----+
| alice | 30 |
+-------+-----+
# Markdown style (for documentation)
echo '[{"name": "alice", "age": 30}, {"name": "bob", "age": 25}]' | jpx '@' -t --table-style markdown
| name | age |
|-------|-----|
| alice | 30 |
| bob | 25 |
Use Cases
- Quick data inspection in the terminal
- Generating markdown tables for documentation
- Human-readable output for reports
# Display users as a table
jpx '[*].{name: name, email: email, role: role}' -f users.json --table
# Generate markdown documentation
jpx '[*].{Function: name, Description: description}' -f functions.json -t --table-style markdown > docs/functions.md
Combining with Expressions
Output formats work with any JMESPath expression:
# Extract specific fields and output as YAML
echo '[{"name": "alice", "age": 30, "email": "alice@example.com"}]' | \
jpx '[*].{name: name, email: email}' --yaml
- email: alice@example.com
name: alice
# Filter and export as CSV
jpx '[?status == `active`].{id: id, name: name}' -f users.json --csv
# Aggregate and output as TOML
jpx '{total: length(@), active: length([?active])}' -f users.json --toml
active = 42
total = 100
Output to Files
All output formats work with the -o flag:
# Save as YAML
jpx '@' -f config.json --yaml -o config.yaml
# Save as CSV
jpx '[*].{name: name, email: email}' -f users.json --csv -o contacts.csv
# Save as TOML
jpx '@' -f settings.json --toml -o settings.toml
Format Selection Tips
| Use Case | Recommended Format |
|---|---|
| Human-readable config | --yaml |
| Rust/Python config files | --toml |
| Spreadsheet export | --csv |
| Unix pipeline processing | --tsv or --lines |
| Streaming/logging | --lines |
| Database import | --csv |
| API debugging | --yaml |
| Terminal display | --table |
| Markdown documentation | --table --table-style markdown |
Expression Syntax
JMESPath expression syntax reference.
Identifiers
Access object properties by name:
echo '{"name": "Alice", "age": 30}' | jpx 'name'
# "Alice"
Index Expressions
Access array elements by index (0-based):
echo '{"items": ["a", "b", "c"]}' | jpx 'items[0]'
# "a"
echo '{"items": ["a", "b", "c"]}' | jpx 'items[-1]'
# "c"
Slicing
Extract array slices with [start:stop:step]:
echo '[0, 1, 2, 3, 4, 5]' | jpx '[0:3]'
# [0, 1, 2]
echo '[0, 1, 2, 3, 4, 5]' | jpx '[::2]'
# [0, 2, 4]
echo '[0, 1, 2, 3, 4, 5]' | jpx '[::-1]'
# [5, 4, 3, 2, 1, 0]
Wildcard
Project all elements with *:
echo '{"users": [{"name": "Alice"}, {"name": "Bob"}]}' | jpx 'users[*].name'
# ["Alice", "Bob"]
Flatten
Flatten nested arrays with []:
echo '[[1, 2], [3, 4]]' | jpx '[]'
# [1, 2, 3, 4]
Filter Expressions
Filter arrays with [?expression]:
echo '[{"age": 25}, {"age": 17}, {"age": 30}]' | jpx '[?age > `18`]'
# [{"age": 25}, {"age": 30}]
echo '[{"name": "Alice", "active": true}, {"name": "Bob", "active": false}]' | jpx '[?active].name'
# ["Alice"]
Comparison Operators
| Operator | Description |
|---|---|
== | Equal |
!= | Not equal |
< | Less than |
<= | Less than or equal |
> | Greater than |
>= | Greater than or equal |
Logical Operators
| Operator | Description |
|---|---|
&& | Logical AND |
|| | Logical OR |
! | Logical NOT |
Pipe Expressions
Chain expressions with |:
echo '{"items": [3, 1, 2]}' | jpx 'items | sort(@) | reverse(@)'
# [3, 2, 1]
Multi-Select List
Create arrays from multiple expressions:
echo '{"a": 1, "b": 2, "c": 3}' | jpx '[a, b]'
# [1, 2]
Multi-Select Hash
Create objects from multiple expressions:
echo '{"first": "Alice", "last": "Smith", "age": 30}' | jpx '{name: first, surname: last}'
# {"name": "Alice", "surname": "Smith"}
Literal Values
Use backticks for literal values:
# Literal number
echo '{"items": [1, 2, 3]}' | jpx '[?@ > `1`]'
# Literal string
echo '{"items": ["a", "b"]}' | jpx 'contains(items, `"a"`)'
# Literal array
echo '{}' | jpx '`[1, 2, 3]`'
# Literal object
echo '{}' | jpx '`{"key": "value"}`'
Current Node
Reference the current node with @:
echo '[1, 2, 3, 4, 5]' | jpx '[?@ > `2`]'
# [3, 4, 5]
echo '"hello"' | jpx 'upper(@)'
# "HELLO"
Function Calls
Call functions with function_name(arg1, arg2, ...):
echo '{"items": [1, 2, 3]}' | jpx 'length(items)'
# 3
echo '{"name": "hello"}' | jpx 'upper(name)'
# "HELLO"
echo '{"a": [1, 2], "b": [3, 4]}' | jpx 'merge(a, b)'
# [1, 2, 3, 4]
Functions can take literal JSON values as arguments using backticks:
# Apply default values to an object
echo '{"name": "Alice"}' | jpx 'defaults(@, `{"role": "user", "active": true}`)'
# {"name": "Alice", "role": "user", "active": true}
# Build an object from key-value pairs
echo 'null' | jpx 'from_items(`[["a", 1], ["b", 2]]`)'
# {"a": 1, "b": 2}
Expression References
Use & for expression references (used with higher-order functions):
echo '[{"age": 30}, {"age": 20}]' | jpx 'sort_by(@, &age)'
# [{"age": 20}, {"age": 30}]
echo '[1, 2, 3]' | jpx 'map(&@ * `2`, @)'
# [2, 4, 6]
String Literals and Escaping
This guide covers how to properly write string literals, regex patterns, and special characters in JMESPath expressions. Understanding escaping is crucial for working with split(), replace(), regex functions, and any operation involving literal strings.
The Golden Rule
In JMESPath:
- Single quotes
'...'= Identifiers (field names) - Backticks with JSON
`"..."`= String literals
# WRONG - 'hello' is an identifier (looks for a field named "hello")
echo '{"hello": "world"}' | jpx "'hello'"
# null (unless there's a field whose value equals the string "hello")
# RIGHT - `"hello"` is a string literal
echo '{"data": "hello"}' | jpx 'data == `"hello"`'
# true
String Literals Quick Reference
| What you want | JMESPath syntax | Shell example |
|---|---|---|
String hello | `"hello"` | jpx 'length(“hello”)' --null-input |
| String with spaces | `"hello world"` | jpx 'length(“hello world”)' --null-input |
| Empty string | `""` | jpx '@ == “”' --null-input |
Number 42 | `42` | jpx '42+8' --null-input |
| Boolean | `true` or `false` | jpx 'true' --null-input |
| Null | `null` | jpx 'null' --null-input |
Special Characters
Newlines, Tabs, and Escapes
Inside backtick literals, use standard JSON escape sequences:
| Character | JMESPath syntax | Example |
|---|---|---|
| Newline | `"\n"` | split(@, “\n”) |
| Tab | `"\t"` | split(@, “\t”) |
| Carriage return | `"\r"` | replace(@, “\r”, “”) |
| Backslash | `"\\"` | split(@, “\”) |
| Double quote | `"\""` | contains(@, “"”) |
Example: Splitting by Newlines
# Split multi-line text into array
echo '"line1\nline2\nline3"' | jpx 'split(@, `"\n"`)'
# ["line1", "line2", "line3"]
Example: Working with Tabs
# Split TSV (tab-separated values)
echo '"name\tage\tcity"' | jpx 'split(@, `"\t"`)'
# ["name", "age", "city"]
Regex Patterns
Regex functions (regex_match, regex_extract, regex_replace, etc.) need their patterns as string literals.
Regex Quick Reference
| Regex pattern | JMESPath syntax | What it matches |
|---|---|---|
\d+ (digits) | `"\\d+"` | One or more digits |
\w+ (word chars) | `"\\w+"` | Word characters |
\s+ (whitespace) | `"\\s+"` | Whitespace |
[a-z]+ | `"[a-z]+"` | Lowercase letters |
^start | `"^start"` | Starts with “start” |
end$ | `"end$"` | Ends with “end” |
Key insight: Backslash escapes in regex patterns need to be escaped for JSON, so \d becomes \\d inside the backtick literal.
Example: Extract Numbers
echo '"Order #12345"' | jpx 'regex_extract(@, `"\\d+"`)'
# "12345"
Example: Match Email Pattern
echo '"contact@example.com"' | jpx 'regex_match(@, `"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}"`)'
# true
Example: Replace Whitespace
echo '"hello world"' | jpx 'regex_replace(@, `"\\s+"`, `" "`)'
# "hello world"
Shell Escaping Layers
When running jpx from a shell, you have multiple escaping layers:
- Shell interprets your command line
- JMESPath parses the expression
- JSON (inside backticks) parses the literal
- Regex engine (for regex functions) interprets the pattern
Shell Quoting Strategies
Single quotes (recommended) - Shell doesn’t interpret anything:
# Best: Use single quotes around the entire expression
echo '{"text": "hello"}' | jpx 'regex_match(text, `"\\w+"`)'
Double quotes - Need to escape backticks and backslashes:
# Harder: Must escape backticks and double backslashes
echo '{"text": "hello"}' | jpx "regex_match(text, \`\"\\\\w+\"\`)"
Recommendation
Always use single quotes around your JMESPath expression when possible:
jpx 'your_expression_here'
This lets you write backticks and backslashes without shell interference.
Common Mistakes
Mistake 1: Using Single Quotes for Strings
# WRONG - 'hello' is an identifier
echo '{"x": "hello"}' | jpx "x == 'hello'"
# RIGHT - use backtick literal
echo '{"x": "hello"}' | jpx 'x == `"hello"`'
Mistake 2: Forgetting Quotes Inside Backticks
# WRONG - `hello` is not valid JSON
jpx '`hello`' --null-input
# RIGHT - strings need quotes inside backticks
jpx '`"hello"`' --null-input
Mistake 3: Over-escaping Regex Patterns
# WRONG - too many backslashes
echo '"abc123"' | jpx 'regex_extract(@, `"\\\\d+"`)'
# RIGHT - just escape once for JSON
echo '"abc123"' | jpx 'regex_extract(@, `"\\d+"`)'
Mistake 4: Confusing Identifier vs String
# This looks for field named "name" - CORRECT
echo '{"name": "Alice"}' | jpx 'name'
# "Alice"
# This compares against literal string - CORRECT
echo '{"name": "Alice"}' | jpx 'name == `"Alice"`'
# true
# This looks for field named by VALUE of 'name' field - WRONG (usually)
echo '{"name": "Alice"}' | jpx "'name'"
# null (looks for field "name" on string "Alice")
MCP Server Context
When using jpx through the MCP server (e.g., with Claude), there’s an additional JSON encoding layer. The MCP server handles this automatically, but be aware:
- Expressions are passed as JSON strings
- The server unescapes before evaluating
- When writing expressions in MCP tool calls, you typically don’t need extra escaping
Example MCP Tool Call
{
"name": "jpx_query",
"arguments": {
"expression": "split(@, `\"\\n\"`)",
"data": "line1\nline2\nline3"
}
}
The MCP server handles the JSON string escaping, so \" becomes " and \\n becomes \n in the actual expression.
Debugging Tips
-
Use
--null-inputto test expressions without needing input data:jpx '`"test\\nstring"`' --null-input -
Start simple - verify your literal works before using it in a function:
# First, check the literal jpx '`"\\d+"`' --null-input # "\d+" # Then use it echo '"abc123"' | jpx 'regex_extract(@, `"\\d+"`)' -
Check the AST for complex expressions:
jpx --ast 'split(@, `"\n"`)'
Summary
| Context | Syntax | Example |
|---|---|---|
| Field access | fieldname | user.name |
| String literal | `"string"` | `"hello"` |
| Newline in string | `"\n"` | split(@, “\n”) |
| Regex digit | `"\\d+"` | regex_match(@, “\d+”) |
| Comparison | field == “value”`` | status == “active”`` |
Working with Files
jpx provides several ways to read input and write output, making it flexible for different workflows.
Reading Input
From stdin (default)
Pipe JSON directly to jpx:
echo '{"name": "alice"}' | jpx 'name'
# "alice"
cat data.json | jpx 'users[*].name'
curl -s https://api.example.com/data | jpx 'items[0]'
From a File
Use -f or --file to read from a file:
jpx -f data.json 'users[*].name'
jpx --file config.json 'settings.theme'
Null Input
Use -n or --null-input for expressions that don’t need input data:
jpx -n 'now()'
# "2024-01-15T10:30:00Z"
jpx -n 'uuid()'
# "550e8400-e29b-41d4-a716-446655440000"
jpx -n 'range(1, 5)'
# [1, 2, 3, 4]
Writing Output
To stdout (default)
Output goes to stdout with pretty-printing by default:
jpx -f data.json 'users[0]'
# {
# "name": "alice",
# "age": 30
# }
To a File
Use -o or --output to write to a file:
jpx -f data.json 'users[?active]' -o active-users.json
jpx -f input.json 'transform(@)' --output result.json
Compact Output
Use -c or --compact for single-line JSON (no pretty-printing):
jpx -c -f data.json 'users[0]'
# {"name":"alice","age":30}
Processing Multiple Files
Sequential Processing
Process files one at a time in a loop:
for f in *.json; do
jpx -f "$f" 'metadata.title'
done
Combining Files with Slurp
Use -s or --slurp to combine multiple JSON values into an array:
cat file1.json file2.json file3.json | jpx -s 'length(@)'
# 3
cat *.json | jpx -s '[*].name'
Streaming JSON Lines (NDJSON)
Use --stream (or --each) for newline-delimited JSON:
# Process each line independently
cat events.ndjson | jpx --stream 'event_type'
# Filter and transform streaming data
cat logs.ndjson | jpx --stream '[?level == `error`].message'
Streaming mode uses constant memory regardless of file size.
Query Files
Store queries in files for reuse:
Plain Text Query Files
# query.txt contains: users[?active].{name: name, email: email}
jpx -Q query.txt -f data.json
Query Libraries (.jpx)
Create reusable query collections:
-- queries.jpx
-- :name active-users
-- :description Get all active users
users[?active]
-- :name user-emails
-- :description Extract just the email addresses
users[*].email
Run named queries:
jpx -Q queries.jpx:active-users -f data.json
# Or with --query flag
jpx -Q queries.jpx --query user-emails -f data.json
# List available queries
jpx -Q queries.jpx --list-queries
See Query Files for more details.
JSON Patch Operations
Generate Patches
Compare two files and generate a JSON Patch (RFC 6902):
jpx --diff original.json modified.json > changes.patch
Apply Patches
Apply a patch to a document:
jpx --patch changes.patch -f document.json -o updated.json
Merge Patches
Apply a JSON Merge Patch (RFC 7396):
jpx --merge updates.json -f document.json
Tips
Checking File Validity
Use --stats to quickly validate and analyze a JSON file:
jpx --stats -f data.json
Exploring File Structure
Use --paths to see all paths in a JSON document:
jpx --paths -f data.json
# With types
jpx --paths --types -f data.json
# With values
jpx --paths --values -f data.json
Benchmarking File Operations
Measure query performance on a file:
jpx --bench 1000 -f large-data.json 'complex[*].transformation'
Query Files
Query files let you store JMESPath expressions externally, making them reusable, version-controlled, and easier to maintain. jpx supports two formats:
- Simple query files - A single expression in a plain text file
- Query libraries (.jpx) - Multiple named queries in one file
Simple Query Files
The simplest approach: put your expression in a file and reference it with -Q:
# Create a query file
echo 'users[?active].{name: name, email: email}' > active-users.txt
# Use it
jpx -Q active-users.txt data.json
This is useful for:
- Long, complex expressions you don’t want to retype
- Sharing queries across scripts
- Version-controlling important queries
Query Libraries (.jpx)
For projects with multiple related queries, use a .jpx query library file. This format lets you define named queries with optional descriptions:
-- :name active-users
-- :desc Get all active users with their contact info
users[?active].{name: name, email: email}
-- :name admin-emails
-- :desc Extract just the admin email addresses
users[?role == `admin`].email
-- :name user-stats
-- :desc Summary statistics about users
{
total: length(users),
active: length(users[?active]),
admins: length(users[?role == `admin`])
}
File Format
-- :name <name>starts a new query (required)-- :desc <description>adds a description (optional)--other comment lines are ignored- Everything else until the next
-- :nameis the query expression - Multi-line expressions are supported
Using Query Libraries
There are two ways to run a query from a library:
# Colon syntax (concise)
jpx -Q queries.jpx:active-users data.json
# Separate flag (explicit)
jpx -Q queries.jpx --query active-users data.json
Both are equivalent. Use whichever feels more natural.
Listing Available Queries
See what queries are in a library:
jpx -Q queries.jpx --list-queries
Output:
Queries in queries.jpx:
NAME DESCRIPTION
----------- ----------------------------------------
active-users Get all active users with their contact info
admin-emails Extract just the admin email addresses
user-stats Summary statistics about users
Use: jpx -Q queries.jpx:<query-name> <input>
Validating Queries
Check that all queries in a library are syntactically valid:
jpx -Q queries.jpx --check
Output:
Validating queries.jpx...
✓ active-users
✓ admin-emails
✓ user-stats
All queries valid.
This is useful in CI pipelines to catch syntax errors before deployment.
Real-World Examples
NLP Analysis Library
Create reusable text processing pipelines:
-- :name clean-html
-- :desc Strip HTML tags and normalize whitespace
regex_replace(@, `<[^>]+>`, ` `) | collapse_whitespace(@)
-- :name extract-keywords
-- :desc Get top keywords from text (stemmed, no stopwords)
tokens(@) | remove_stopwords(@) | stems(@) | frequencies(@)
-- :name title-keywords
-- :desc Extract keywords from article titles
hits[*].title | join(` `, @) | tokens(@) | remove_stopwords(@) | stems(@) | frequencies(@)
-- :name reading-stats
-- :desc Get reading time and word count
{
word_count: word_count(@),
reading_time: reading_time(@),
sentence_count: sentence_count(@)
}
Use it:
# Clean HTML from a field
jpx 'story_text' data.json | jpx -Q nlp.jpx:clean-html
# Analyze Hacker News titles
jpx -Q nlp.jpx:title-keywords hn_front.json
API Response Processing
Standardize how you extract data from APIs:
-- :name github-repos
-- :desc Extract repo summary from GitHub API response
[*].{
name: name,
stars: stargazers_count,
language: language,
description: description | default(@, `"No description"`)
}
-- :name github-issues
-- :desc Format GitHub issues for display
[*].{
number: number,
title: title,
state: state,
author: user.login,
labels: labels[*].name | join(`, `, @)
}
-- :name paginated-total
-- :desc Get total from paginated API response
{
count: length(items),
total: total_count,
has_more: length(items) < total_count
}
Data Transformation Library
Common transformations for ETL pipelines:
-- :name flatten-nested
-- :desc Flatten nested user records for CSV export
[*].{
id: id,
name: profile.name,
email: profile.email,
city: profile.address.city,
country: profile.address.country,
created: metadata.created_at
}
-- :name aggregate-by-status
-- :desc Group and count records by status
group_by(@, &status) | map(&{ status: [0].status, count: length(@) }, @)
-- :name enrich-timestamps
-- :desc Add formatted date fields
[*] | map(&merge(@, {
created_date: format_datetime(created_at, `%Y-%m-%d`),
created_time: format_datetime(created_at, `%H:%M:%S`)
}), @)
Log Analysis Library
Queries for processing structured logs:
-- :name errors-only
-- :desc Filter to just error-level logs
[?level == `error` || level == `ERROR`]
-- :name errors-by-service
-- :desc Count errors grouped by service name
[?level == `error`] | group_by(@, &service) | map(&{ service: [0].service, count: length(@) }, @)
-- :name recent-errors
-- :desc Errors from the last hour with context
[?level == `error`] | sort_by(@, ×tamp) | reverse(@) | [:20].{
time: timestamp,
service: service,
message: message,
trace_id: trace_id
}
-- :name slow-requests
-- :desc Requests taking longer than 1 second
[?duration_ms > `1000`] | sort_by(@, &duration_ms) | reverse(@)
Best Practices
Naming Conventions
Use clear, descriptive names:
active-usersnotauorquery1errors-by-servicenoterr-svc- Use kebab-case for consistency
Add Descriptions
Always add -- :desc lines. They show up in --list-queries and help others (and future you) understand what each query does.
Organize by Domain
Group related queries into domain-specific libraries:
nlp.jpx- Text processing pipelinesapi.jpx- API response transformationslogs.jpx- Log analysis queriesetl.jpx- Data transformation queries
Version Control
Query libraries are plain text files - perfect for git:
- Track changes to important queries
- Review query changes in PRs
- Share queries across your team
Validate in CI
Add query validation to your CI pipeline:
# .github/workflows/validate.yml
- name: Validate query libraries
run: |
for f in queries/*.jpx; do
jpx -Q "$f" --check || exit 1
done
CLI Reference
| Option | Description |
|---|---|
-Q, --query-file <FILE> | Load expression from file |
--query <NAME> | Select a named query from a .jpx library |
--list-queries | List all queries in a .jpx file |
--check | Validate all queries without running |
Colon Syntax
The colon syntax -Q file.jpx:query-name is shorthand for -Q file.jpx --query query-name.
Detection Logic
jpx automatically detects query libraries:
- Files ending in
.jpxare always treated as libraries - Files starting with
-- :nameare treated as libraries - Everything else is a simple single-query file
Migration from Simple Files
If you have many simple query files, consolidate them:
# Before: multiple files
queries/
active-users.txt
admin-emails.txt
user-stats.txt
# After: one library
queries/users.jpx
Just add -- :name headers to combine them:
-- :name active-users
users[?active].{name: name, email: email}
-- :name admin-emails
users[?role == `admin`].email
-- :name user-stats
{total: length(users), active: length(users[?active])}
Strict Mode
Strict mode disables all extension functions, limiting jpx to only the 26 standard JMESPath functions. This is useful for writing portable queries that work with any JMESPath implementation.
Enabling Strict Mode
Use the --strict flag or set the JPX_STRICT=1 environment variable:
# Via flag
echo '{"name": "alice"}' | jpx --strict 'upper(name)'
# Error: Unknown function: upper
# Via environment variable
export JPX_STRICT=1
echo '{"items": [1, 2, 3]}' | jpx 'length(items)'
# 3
Standard JMESPath Functions
In strict mode, only these functions are available:
| Function | Description |
|---|---|
abs(n) | Absolute value |
avg(array) | Average of numbers |
ceil(n) | Round up |
contains(subject, search) | Check if contains value |
ends_with(str, suffix) | Check string suffix |
floor(n) | Round down |
join(glue, array) | Join strings |
keys(obj) | Object keys |
length(subject) | Length of array/string/object |
map(&expr, array) | Transform each element |
max(array) | Maximum value |
max_by(array, &expr) | Maximum by expression |
merge(obj1, obj2, ...) | Merge objects |
min(array) | Minimum value |
min_by(array, &expr) | Minimum by expression |
not_null(arg1, arg2, ...) | First non-null value |
reverse(array) | Reverse array |
sort(array) | Sort array |
sort_by(array, &expr) | Sort by expression |
starts_with(str, prefix) | Check string prefix |
sum(array) | Sum of numbers |
to_array(arg) | Convert to array |
to_number(arg) | Convert to number |
to_string(arg) | Convert to string |
type(arg) | Get type name |
values(obj) | Object values |
Use Cases
Portable Queries
If you’re writing queries that need to work across different JMESPath implementations (AWS CLI, Python jmespath, etc.), use strict mode to ensure compatibility:
# Test your query works with standard JMESPath
jpx --strict '[?status == `active`].name | sort(@)'
CI/CD Validation
Ensure queries in your codebase don’t accidentally use extensions:
# In your CI pipeline
jpx --strict -Q queries.jpx --check
Gradual Migration
When migrating from another JMESPath tool, start in strict mode and gradually adopt extensions as needed.
Checking if an Expression Uses Extensions
Use --explain to see what functions an expression uses:
jpx --explain 'users[*].name | unique(@) | sort(@)'
The unique function is an extension, while sort is standard.
MCP Server Strict Mode
The MCP server also supports strict mode. When configured with --strict, only standard JMESPath functions are available:
{
"mcpServers": {
"jpx-strict": {
"command": "jpx-server",
"args": ["--strict"]
}
}
}
Why jpx?
“I could just write Python or JavaScript for this - why use jpx?”
This is a fair question. Here’s why jpx is worth learning for JSON wrangling.
Almost No Syntax
JMESPath’s entire syntax fits on an index card:
| Syntax | Example | Meaning |
|---|---|---|
| Dot access | foo.bar | Get nested field |
| Brackets | [0], [*], [?filter] | Index, project, filter |
| Pipe | a | b | Chain expressions |
| Functions | name(args) | Call a function |
| Literals | `"string"`, `123`, `true` | Literal values |
That’s it. No for, if, def, import, class, let, const, =>, async, await…
You can learn the entire language in 10 minutes. The power comes from composing simple pieces, not memorizing syntax - the same insight that makes Lisp and shell pipelines compelling.
Compare to Python where “I want to extract some fields” requires knowing:
- Variable assignment
- Import statements
- List comprehensions (or for loops)
- Dictionary access
- String formatting
- File I/O
Zero Boilerplate
No imports. No script files. No virtual environments. Just pipe and go:
curl -s https://api.example.com/users | jpx 'users[*].name'
Compare to the Python equivalent:
import json
import sys
data = json.load(sys.stdin)
names = [user["name"] for user in data["users"]]
print(json.dumps(names))
That’s 5 lines (plus saving to a file, making it executable, etc.) vs. a single shell command.
Composable One-Liners
Shell pipelines are underrated. With jpx, you can do complex transformations in a single expression:
# Fetch, filter, transform, and sort in one line
curl -s https://api.github.com/users/octocat/repos \
| jpx '[?stargazers_count > `100`] | sort_by(@, &stargazers_count) | reverse(@) | [*].{name: name, stars: stargazers_count}'
Or break it into multiple jpx calls when debugging:
curl -s https://api.github.com/users/octocat/repos \
| jpx '[?stargazers_count > `100`]' \
| jpx 'sort_by(@, &stargazers_count) | reverse(@) | [*].{name: name, stars: stargazers_count}'
Each step is independently testable. Add or remove transformations without rewriting a script.
Declarative vs Imperative
You describe what you want, not how to loop through it:
Imperative (Python):
result = []
for item in data["items"]:
if item["active"]:
result.append({"n": item["name"], "v": item["value"]})
Declarative (jpx):
jpx 'items[?active].{n: name, v: value}'
The jpx version is often more readable because it expresses intent directly.
The Learning Curve Comparison
| Task | Python | jpx |
|---|---|---|
| Get nested field | data["users"][0]["name"] | users[0].name |
| Filter array | [x for x in items if x["active"]] | items[?active] |
| Transform all | [{"n": x["name"]} for x in items] | items[*].{n: name} |
| Chain operations | Multiple lines or nested calls | a | b | c |
| Sort by field | sorted(items, key=lambda x: x["age"]) | sort_by(items, &age) |
Consistent Interface
Same syntax across:
- CLI (
jpx 'query') - MCP server (for AI assistants)
- Python bindings (
jmespath_extensions.search('query', data)) - Rust library
Learn once, use everywhere.
No Runtime Dependencies
jpx is a single ~15MB binary. It works on any machine without Python, Node, or anything else installed.
- Great for CI/CD pipelines
- Works in minimal containers
- No “works on my machine” issues
- No dependency conflicts
Built for Exploration
When you’re exploring an unfamiliar API response, jpx helps you discover what’s there:
# Interactive REPL
jpx --repl
# See all paths in the data
jpx --paths -f data.json
# Search for functions
jpx --search "date"
# Get help on any function
jpx --describe format_date
# Find similar functions
jpx --similar levenshtein
Python requires knowing what to import before you start. jpx lets you explore first.
Safe for Automation
JMESPath expressions are pure functions with no side effects:
- No “oops I overwrote my file” moments
- No network calls from within queries
- No shell injection vulnerabilities
- Predictable, deterministic behavior
This makes jpx safe to use in scripts and automation.
When to Use What
jpx is for ad-hoc JSON exploration and transformation:
- “What’s in this API response?”
- “Extract these fields from 50 log files”
- “Quick data munging in a shell script”
- “Prototype a transformation before implementing it”
Use jpx when:
- Quick field extraction from JSON
- Ad-hoc API exploration
- Shell script data processing
- CI/CD pipeline JSON manipulation
- You need to explain the query to someone else
Use Python/JavaScript when:
- Complex business logic with many conditionals
- Stateful transformations
- Database operations
- Production ETL pipelines
- You need to integrate with other services
The sweet spot: use jpx for the JSON transformation step inside larger Python/JS applications:
import jmespath_extensions
# Complex Python logic here...
data = fetch_from_api()
# Let jpx handle the transformation
result = jmespath_extensions.search('items[?active].{id: id, name: upper(name)}', data)
# More Python logic...
save_to_database(result)
Coming from jq?
The key syntax differences:
| jq | jpx |
|---|---|
.[].name | [*].name |
select(.age > 30) | [?age > \30`]` |
length | length(@) |
jpx has more built-in functions (400+). jq is better for complex recursive transformations.
Summary
jpx trades generality for ergonomics. It can’t do everything Python can, but for its intended purpose - querying and transforming JSON - it’s faster to write, easier to read, and simpler to maintain.
The 10 minutes you spend learning JMESPath will save you hours of writing boilerplate data munging code.
MCP Server Overview
jpx can run as an MCP (Model Context Protocol) server, allowing AI assistants like Claude to use JMESPath for JSON querying and transformation.
What is MCP?
MCP is an open protocol that enables AI assistants to interact with external tools and data sources. By running jpx as an MCP server, you give Claude (and other MCP-compatible assistants) the ability to:
- Query JSON data using JMESPath expressions
- Transform and manipulate JSON structures
- Use all 400+ extension functions
- Explore available functions and their documentation
Why Use jpx with Claude?
When working with JSON data in Claude, jpx provides:
- Precise queries: Extract exactly the data you need
- Complex transformations: Reshape data structures on the fly
- Powerful functions: String manipulation, math, dates, hashing, and more
- Consistent results: Deterministic query execution
Available Tools
The MCP server exposes 25 tools organized by purpose:
Function Discovery Tools
Tools for finding and exploring available JMESPath functions:
| Tool | Description |
|---|---|
search | Fuzzy search for functions by name, description, category, or signature |
similar | Find functions related to a specified function |
functions | List available functions (optionally filter by category) |
describe | Get detailed info for a specific function |
categories | List all function categories |
Multi-Server Tool Discovery
Tools for semantic search across multiple MCP servers (see Discovery):
| Tool | Description |
|---|---|
register_discovery | Register an MCP server’s tools for indexing |
query_tools | BM25 semantic search across registered tools |
similar_tools | Find tools related to a specific tool |
list_discovery_servers | List registered servers |
list_discovery_categories | List tool categories across servers |
inspect_discovery_index | Debug index statistics |
unregister_discovery | Remove a server from the registry |
get_discovery_schema | Get the registration schema |
Data Analysis Tools
Tools for understanding JSON structure before querying:
| Tool | Description |
|---|---|
stats | Analyze JSON structure (type, size, depth, field analysis) |
paths | Extract all paths in dot notation (e.g., users[0].name) |
keys | Extract object keys (optionally recursive with dot notation) |
Query Tools
Tools for evaluating JMESPath expressions:
| Tool | Description |
|---|---|
evaluate | Run a JMESPath expression against JSON input |
evaluate_file | Query JSON files directly from disk |
batch_evaluate | Run multiple expressions against the same input |
validate | Check expression syntax without executing |
JSON Utility Tools
Tools for JSON manipulation (RFC 6902/7396):
| Tool | Description |
|---|---|
format | Pretty-print JSON with configurable indentation |
diff | Generate RFC 6902 JSON Patch between documents |
patch | Apply RFC 6902 JSON Patch operations |
merge | Apply RFC 7396 JSON Merge Patch |
Typical Workflow for AI Agents
- Analyze data: Use
statsandpathsto understand the JSON structure - Discover functions: Use
searchto find relevant functions,similarto explore alternatives - Build query: Use
validateto check syntax before executing - Execute: Use
evaluateorbatch_evaluateto run queries - Transform: Use
diff,patch, ormergefor modifications
Getting Started
See Setup to configure jpx as an MCP server for Claude Desktop.
Setup with Claude Desktop
Configure jpx-server as an MCP server for Claude Desktop.
Prerequisites
-
Install jpx-server:
cargo install jpx-serverOr use Docker (no installation required).
-
Have Claude Desktop installed
Configuration
Docker (Recommended)
The simplest way to run jpx as an MCP server using the dedicated server image:
{
"mcpServers": {
"jpx": {
"command": "docker",
"args": ["run", "-i", "--rm", "ghcr.io/joshrotenberg/jpx-server"]
}
}
}
Available for linux/amd64 and linux/arm64.
macOS
Edit ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"jpx": {
"command": "jpx-server"
}
}
}
If jpx-server is not in your PATH, use the full path:
{
"mcpServers": {
"jpx": {
"command": "/Users/yourname/.cargo/bin/jpx-server"
}
}
}
Windows
Edit %APPDATA%\Claude\claude_desktop_config.json:
{
"mcpServers": {
"jpx": {
"command": "C:\\path\\to\\jpx-server.exe"
}
}
}
Verify Setup
- Restart Claude Desktop
- Look for the jpx tools in Claude’s tool list
- Try a simple query:
User: I have this JSON: {"users": [{"name": "alice"}, {"name": "bob"}]}
Get all the names.
Claude: [Uses jpx.evaluate]
Result: ["alice", "bob"]
Strict Mode
To use only standard JMESPath functions (no extensions):
{
"mcpServers": {
"jpx": {
"command": "jpx-server",
"args": ["--strict"]
}
}
}
Or with Docker:
{
"mcpServers": {
"jpx": {
"command": "docker",
"args": ["run", "-i", "--rm", "ghcr.io/joshrotenberg/jpx-server", "--strict"]
}
}
}
Troubleshooting
jpx-server not found
Make sure jpx-server is in your PATH, or use the full path in the config.
Tools not appearing
- Check the config file syntax (must be valid JSON)
- Restart Claude Desktop completely
- Check Claude Desktop logs for errors
Permission errors on file access
The evaluate_file tool has security restrictions. It only allows access to files in safe directories (not system paths).
Available Tools
The jpx MCP server provides 26 tools organized into five categories.
Function Discovery
Tools for exploring the 400+ JMESPath extension functions.
search
Fuzzy search for functions by name, description, category, or signature.
{
"query": "string manipulation",
"limit": 10
}
similar
Find functions related to a specified function.
{
"function": "upper",
"limit": 5
}
Returns functions with similar purpose (e.g., lower, capitalize, title_case).
functions
List available functions, optionally filtered by category.
{
"category": "string"
}
describe
Get detailed documentation for a specific function.
{
"function": "pad_left"
}
Returns signature, description, parameters, return type, and examples.
categories
List all function categories with tool counts.
{}
Data Analysis
Tools for understanding JSON structure before writing queries.
stats
Analyze JSON structure and statistics.
{
"json": {"users": [{"name": "Alice"}, {"name": "Bob"}]}
}
Returns type, size, depth, and field analysis.
paths
Extract all paths in a JSON document using dot notation.
{
"json": {"user": {"profile": {"name": "Alice"}}}
}
Returns: ["user", "user.profile", "user.profile.name"]
keys
Extract object keys, optionally recursive with dot notation.
{
"json": {"a": {"b": 1}},
"recursive": true
}
Query Execution
Tools for running JMESPath expressions.
evaluate
Run a JMESPath expression against JSON input.
{
"expression": "users[?age > `30`].name",
"json": {"users": [{"name": "Alice", "age": 35}, {"name": "Bob", "age": 25}]}
}
Returns: ["Alice"]
evaluate_file
Query a JSON file directly from disk. Useful when the file is too large to pass inline or when you want to avoid serialization overhead.
{
"expression": "length(items)",
"path": "/path/to/data.json"
}
batch_evaluate
Run multiple expressions against the same input.
{
"expressions": ["length(@)", "keys(@)", "@.name"],
"json": {"name": "test", "value": 42}
}
validate
Check expression syntax without executing.
{
"expression": "users[?active].name"
}
Returns validation status and any syntax errors.
JSON Utilities
Tools for JSON manipulation following RFC standards (RFC 6902 for JSON Patch, RFC 7396 for Merge Patch).
format
Pretty-print JSON with configurable indentation.
{
"json": {"compact": true},
"indent": 2
}
diff
Generate RFC 6902 JSON Patch between two documents.
{
"source": {"a": 1},
"target": {"a": 2, "b": 3}
}
Returns patch operations: [{"op": "replace", "path": "/a", "value": 2}, {"op": "add", "path": "/b", "value": 3}]
patch
Apply RFC 6902 JSON Patch operations.
{
"json": {"a": 1},
"operations": [{"op": "add", "path": "/b", "value": 2}]
}
merge
Apply RFC 7396 JSON Merge Patch.
{
"json": {"a": 1, "b": 2},
"patch": {"b": null, "c": 3}
}
Returns: {"a": 1, "c": 3} (null removes keys)
Multi-Server Discovery
Tools for semantic search across multiple MCP servers. See Multi-Server Discovery for full documentation.
register_discovery
Register an MCP server’s tools for BM25 indexing using the full discovery spec.
{
"spec": {
"server": {"name": "myserver", "version": "1.0.0"},
"tools": [
{"name": "tool_one", "description": "Does something useful", "category": "utils"}
]
}
}
register_tools_simple
Quick registration without the full schema. Just provide server name and tools array.
{
"server_name": "myserver",
"tools": [
{"name": "backup_db", "description": "Backup the database", "tags": ["database", "backup"]},
{"name": "restore_db", "description": "Restore from backup", "tags": ["database", "restore"]}
]
}
query_tools
Semantic search across all registered tools.
{
"query": "backup database",
"top_k": 5
}
Returns ranked results with BM25 scores.
similar_tools
Find tools related to a specific tool.
{
"tool_id": "myserver:tool_one",
"top_k": 5
}
list_discovery_servers
List all registered servers with tool counts.
{}
list_discovery_categories
List tool categories across all registered servers.
{}
inspect_discovery_index
Get index statistics for debugging.
{}
Returns server count, tool count, term count, and average document length.
unregister_discovery
Remove a server from the registry.
{
"server": "myserver"
}
get_discovery_schema
Get the JSON schema for registration.
{}
Multi-Server Tool Discovery
jpx provides BM25-based semantic search for discovering tools across multiple MCP servers. This enables natural language queries like “how do I backup a database?” instead of scanning flat tool lists.
The Problem
When an agent connects to multiple MCP servers, it has access to dozens or hundreds of tools. Finding the right tool for a task means:
- Scanning tool names hoping for a match
- Reading descriptions one by one
- Missing tools with different terminology (“backup” vs “snapshot” vs “export”)
The Solution: Registration + Search
jpx provides a discovery registry that indexes tools from any MCP server using BM25 (the algorithm behind search engines). The workflow:
- Register: Agent registers each server’s tools with jpx
- Search: Agent queries across all registered tools semantically
- Discover: BM25 finds relevant tools even with different wording
Architecture
MCP servers don’t communicate with each other - they only respond to the agent. The agent orchestrates registration:
┌─────────┐ ┌─────────────┐ ┌─────────────┐
│ Agent │────▶│ jpx │ │ redisctl │
│ │────▶│ (registry) │ │ (65 tools) │
│ │────▶│ │ └─────────────┘
│ │ └─────────────┘ ┌─────────────┐
│ │────────────────────────▶│ other-mcp │
│ │ │ (N tools) │
└─────────┘ └─────────────┘
│
│ 1. Connect to all servers
│ 2. Get tool metadata from each
│ 3. Register with jpx
│ 4. Query jpx for tool discovery
▼
Discovery Tools
Registration
| Tool | Description |
|---|---|
register_discovery | Register an MCP server’s tools |
unregister_discovery | Remove a server from the registry |
get_discovery_schema | Get the registration schema |
Search
| Tool | Description |
|---|---|
query_tools | BM25 semantic search across all registered tools |
similar_tools | Find tools related to a specific tool |
Browse
| Tool | Description |
|---|---|
list_discovery_servers | List all registered servers |
list_discovery_categories | List tool categories across servers |
inspect_discovery_index | Debug index statistics |
Registration Schema
Register a server’s tools with register_discovery:
{
"spec": {
"server": {
"name": "redisctl",
"version": "0.1.0",
"description": "Redis management tools"
},
"tools": [
{
"name": "enterprise_database_create",
"description": "Create a new Redis Enterprise database with specified configuration",
"category": "enterprise",
"subcategory": "database",
"tags": ["database", "create", "write"],
"examples": ["Create a 500MB database named 'cache'"]
},
{
"name": "enterprise_database_backup",
"description": "Trigger a backup snapshot of a Redis Enterprise database",
"category": "enterprise",
"subcategory": "database",
"tags": ["database", "backup", "snapshot", "read"]
}
],
"categories": {
"enterprise": {
"description": "Redis Enterprise on-premises tools",
"tool_count": 30
}
}
},
"replace": false
}
Minimal Schema
Only server.name and tools[].name are required:
{
"spec": {
"server": { "name": "myserver" },
"tools": [
{ "name": "tool_one" },
{ "name": "tool_two" }
]
}
}
Full Schema
For best search results, include rich metadata:
description: Natural language description (indexed for search)category/subcategory: For browsing and filteringtags: Keywords that improve search relevanceexamples: Usage examples (indexed for search)inputSchema: Parameter documentation
Searching
Semantic Search
// query_tools
{
"query": "backup database",
"top_k": 5
}
Returns ranked results with scores:
[
{
"tool_id": "redisctl:enterprise_database_backup",
"server": "redisctl",
"name": "enterprise_database_backup",
"description": "Trigger a backup snapshot of a Redis Enterprise database",
"score": 8.42,
"category": "enterprise"
},
{
"tool_id": "redisctl:enterprise_database_export",
"server": "redisctl",
"name": "enterprise_database_export",
"description": "Export database data to RDB file",
"score": 5.17,
"category": "enterprise"
}
]
Similar Tools
Find tools related to one you know:
// similar_tools
{
"tool_id": "redisctl:enterprise_database_delete",
"top_k": 5
}
Returns tools with similar descriptions/tags - useful for finding alternatives or related operations.
Agent Workflow
Manual Registration
At session start, register each server:
1. Get tool list from redisctl (agent already has this via MCP)
2. Call jpx.register_discovery with redisctl's tools
3. Repeat for other MCP servers
4. Use query_tools for semantic search
Automatic Registration (Recommended)
Add to your agent’s system prompt or CLAUDE.md:
## MCP Tool Discovery
At session start, if jpx and other MCP servers are available:
1. For each non-jpx MCP server, register its tools with jpx:
- Extract tool name, description, and input schema
- Call `mcp__jpx__register_discovery` with the server name and tool list
2. When searching for tools, use `mcp__jpx__query_tools` for semantic search
instead of scanning the tool list manually.
3. Use `mcp__jpx__similar_tools` to find related tools when exploring options.
Example: Multi-Server Setup
With jpx + redisctl + github-mcp:
// Register redisctl (65 tools)
register_discovery({
spec: {
server: { name: "redisctl", version: "0.1.0" },
tools: [
{ name: "enterprise_database_create", description: "...", category: "enterprise" },
{ name: "enterprise_database_backup", description: "...", category: "enterprise" },
// ... 63 more tools
]
}
})
// Register github-mcp (20 tools)
register_discovery({
spec: {
server: { name: "github", version: "1.0.0" },
tools: [
{ name: "create_issue", description: "Create a GitHub issue", category: "issues" },
{ name: "list_pull_requests", description: "List PRs for a repo", category: "pulls" },
// ... 18 more tools
]
}
})
// Now search across both!
query_tools({ query: "create new resource", top_k: 10 })
// Returns: enterprise_database_create, create_issue, enterprise_crdb_create, ...
BM25 Scoring
The search uses BM25, a proven ranking algorithm that:
- Weights rare terms higher: “kubernetes” is more distinctive than “create”
- Handles term frequency saturation: Repeating a word doesn’t linearly increase relevance
- Normalizes by document length: Short and long descriptions scored fairly
This means “backup database” finds tools mentioning “snapshot”, “export”, “persist” if those terms appear in similar contexts across the corpus.
Session Scope
The registry is session-scoped (in-memory):
- Fresh index each agent session
- No persistence across sessions
- Cheap to rebuild from source
This is intentional - tool metadata changes, and re-registration ensures the index matches current reality.
Inspecting the Index
Debug with inspect_discovery_index:
{
"server_count": 2,
"tool_count": 85,
"term_count": 342,
"avg_doc_length": 12.4,
"servers": ["redisctl", "github"]
}
List categories with list_discovery_categories:
{
"enterprise": { "tool_count": 30, "servers": ["redisctl"] },
"cloud": { "tool_count": 25, "servers": ["redisctl"] },
"issues": { "tool_count": 8, "servers": ["github"] },
"pulls": { "tool_count": 6, "servers": ["github"] }
}
MCP Examples
This page demonstrates common workflows using the jpx MCP server with Claude or other AI assistants.
Data Exploration
Understanding Unknown JSON
When you receive unfamiliar JSON data, start by exploring its structure:
User: I have this API response, can you help me understand it?
Claude: Let me analyze the structure first.
[Uses stats tool]
{
"input": "{\"users\": [{\"id\": 1, \"profile\": {\"name\": \"Alice\", \"settings\": {\"theme\": \"dark\"}}}]}"
}
Result: Object with 1 key, depth 4, contains array of 1 object
[Uses paths tool]
{
"input": "{\"users\": [{\"id\": 1, \"profile\": {\"name\": \"Alice\"}}]}",
"include_types": true
}
Result:
users: array
users.0: object
users.0.id: number
users.0.profile: object
users.0.profile.name: string
Finding the Right Function
When you need to transform data but aren’t sure which function to use:
User: I need to remove duplicate entries from an array
Claude: Let me search for relevant functions.
[Uses search tool]
{
"query": "remove duplicates array unique"
}
Result: Found unique(), distinct(), dedupe() - all do similar things.
[Uses describe tool]
{
"name": "unique"
}
Result: unique(array) - Returns array with duplicate elements removed, preserving order.
Query Building
Iterative Query Development
Build complex queries step by step:
User: Help me extract active premium users with their subscription dates
Claude: Let me build this query incrementally.
[Uses evaluate tool - Step 1: Filter active users]
{
"expression": "users[?status == `active`]",
"input": "{\"users\": [{\"name\": \"Alice\", \"status\": \"active\", \"plan\": \"premium\", \"since\": \"2024-01-15\"}, {\"name\": \"Bob\", \"status\": \"inactive\", \"plan\": \"free\", \"since\": \"2023-06-01\"}]}"
}
[Uses evaluate tool - Step 2: Add premium filter]
{
"expression": "users[?status == `active` && plan == `premium`]",
"input": "..."
}
[Uses evaluate tool - Step 3: Project fields]
{
"expression": "users[?status == `active` && plan == `premium`].{name: name, member_since: since}",
"input": "..."
}
Final query: users[?status == `active` && plan == `premium`].{name: name, member_since: since}
Validating Expressions
Check syntax before running on large datasets:
[Uses validate tool]
{
"expression": "users[?active].name | sort(@"
}
Result: Invalid - unclosed parenthesis at position 27
[Uses validate tool]
{
"expression": "users[?active].name | sort(@)"
}
Result: Valid expression
Batch Operations
Multiple Extractions
Extract several pieces of information at once:
[Uses batch_evaluate tool]
{
"input": "{\"items\": [1, 2, 3, 4, 5], \"metadata\": {\"count\": 5, \"type\": \"numbers\"}}",
"expressions": [
"length(items)",
"sum(items)",
"avg(items)",
"metadata.type"
]
}
Result:
length(items): 5
sum(items): 15
avg(items): 3
metadata.type: "numbers"
File Operations
Working with Large Files
Query files directly without loading into memory:
[Uses evaluate_file tool]
{
"file_path": "/path/to/large-dataset.json",
"expression": "records[?timestamp > `2024-01-01`] | length(@)"
}
Result: 15234 records match the filter
JSON Transformations
Generating Patches
Track changes between document versions:
[Uses diff tool]
{
"source": "{\"version\": 1, \"config\": {\"debug\": true, \"timeout\": 30}}",
"target": "{\"version\": 2, \"config\": {\"debug\": false, \"timeout\": 60, \"retries\": 3}}"
}
Result:
[
{"op": "replace", "path": "/version", "value": 2},
{"op": "replace", "path": "/config/debug", "value": false},
{"op": "replace", "path": "/config/timeout", "value": 60},
{"op": "add", "path": "/config/retries", "value": 3}
]
Applying Updates
Merge configuration updates:
[Uses merge tool]
{
"input": "{\"database\": {\"host\": \"localhost\", \"port\": 5432}, \"cache\": {\"enabled\": true}}",
"patch": "{\"database\": {\"host\": \"db.example.com\"}, \"cache\": null}"
}
Result:
{
"database": {"host": "db.example.com", "port": 5432}
}
Note: Setting a key to null in merge patch removes it.
Function Discovery Workflows
Learning New Functions
Explore related functionality:
[Uses similar tool]
{
"function": "snake_case"
}
Result:
Same category: camel_case, kebab_case, pascal_case, title_case
Similar signature: upper, lower, capitalize
Related concepts: snake_keys, camel_keys (for object keys)
Category Exploration
See all functions in a category:
[Uses functions tool]
{
"category": "datetime"
}
Result:
now() - Current timestamp
format_date(date, format) - Format date string
parse_date(string, format) - Parse date from string
date_add(date, amount, unit) - Add time to date
date_diff(date1, date2, unit) - Difference between dates
...
Multi-Server Discovery
When working with multiple MCP servers, use discovery tools to find the right tool across all servers:
[Uses query_tools tool]
{
"query": "send email notification",
"top_k": 5
}
Result:
1. notifications:send_email (score: 12.4)
2. mailer:compose_message (score: 8.2)
3. alerts:notify_user (score: 6.1)
See Multi-Server Discovery for more details on setting up cross-server tool search.
Function Overview
jpx provides 400+ functions organized into 31 categories.
Discovering Functions
List All Functions
jpx --list-functions
List by Category
jpx --list-category string
jpx --list-category math
jpx --list-category datetime
Get Function Details
jpx --describe upper
Categories
| Category | Description | Count |
|---|---|---|
| Array | Functions for working with arrays: chunking, filtering, tran… | 27 |
| Color | Color manipulation and conversion functions. | 8 |
| Computing | Computing-related utility functions. | 9 |
| Date/Time | Functions for working with dates and times: parsing, formatt… | 26 |
| Duration | Functions for working with time durations. | 5 |
| Encoding | Encoding and decoding functions: Base64, hex, URL encoding, … | 8 |
| Expression | Higher-order functions that work with JMESPath expressions a… | 33 |
| Format | Data formatting functions for numbers, currencies, and other… | 6 |
| Fuzzy | Fuzzy matching and string similarity functions. | 9 |
| Geolocation | Geolocation functions: distance calculation, coordinate pars… | 4 |
| Hash | Cryptographic hash functions: MD5, SHA family, and other has… | 9 |
| ID Generation | Functions for generating various types of unique identifiers… | 3 |
| JSON Patch | JSON Patch (RFC 6902) functions: applying patches, generatin… | 3 |
| Language | Natural language processing functions. | 5 |
| Math | Mathematical and statistical functions: arithmetic, rounding… | 35 |
| Multi-Match | Functions for matching multiple patterns or expressions in a… | 10 |
| Network | Network-related functions: IP addresses, CIDR notation, and … | 7 |
| Object | Functions for working with JSON objects: merging, filtering … | 48 |
| Path | File path manipulation functions. | 4 |
| Phonetic | Phonetic encoding functions for sound-based string matching. | 9 |
| Random | Functions for generating random values: numbers, strings, an… | 3 |
| Regular Expression | Regular expression functions: matching, replacing, splitting… | 3 |
| Semantic Versioning | Semantic versioning functions: parsing, comparing, and manip… | 7 |
| Standard JMESPath | These are the standard JMESPath functions as defined in the … | 26 |
| String | Functions for string manipulation: case conversion, splittin… | 36 |
| Text | Text analysis and processing functions. | 11 |
| Type | Type conversion and checking functions. | 13 |
| URL | Functions for parsing and manipulating URLs and their compon… | 3 |
| Utility | General utility functions that don’t fit other categories. | 11 |
| UUID | Functions for generating and working with UUIDs. | 1 |
| Validation | Functions for validating data: email, URL, UUID, and format … | 13 |
Function Syntax
Functions are called with parentheses:
function_name(arg1, arg2, ...)
Examples
# No arguments
echo '{}' | jpx 'now()'
# One argument
echo '{"name": "hello"}' | jpx 'upper(name)'
# Multiple arguments
echo '{"text": "hello world"}' | jpx 'split(text, ` `)'
# Literal arguments (use backticks)
echo '{}' | jpx 'range(`1`, `10`)'
Standard vs Extension Functions
Standard Functions (26)
These are part of the JMESPath specification and work in all implementations:
abs, avg, ceil, contains, ends_with, floor, join, keys, length, map, max, max_by, merge, min, min_by, not_null, reverse, sort, sort_by, starts_with, sum, to_array, to_number, to_string, type, values
Extension Functions (369)
These are jpx-specific and won’t work in other JMESPath implementations.
Strict Mode
Use --strict to disable extension functions:
# This works
jpx --strict 'length(items)' -f data.json
# This fails (upper is an extension)
jpx --strict 'upper(name)' -f data.json
Standard JMESPath Functions
These are the standard JMESPath functions as defined in the specification. They work in all JMESPath implementations.
Summary
| Function | Signature | Description |
|---|---|---|
abs | number -> number | Returns the absolute value of a number |
avg | array[number] -> number | Returns the average of an array of numbers |
ceil | number -> number | Returns the smallest integer greater than or equal to the number |
contains | array|string, any -> boolean | Returns true if the subject contains the search value |
ends_with | string, string -> boolean | Returns true if the subject ends with the suffix |
floor | number -> number | Returns the largest integer less than or equal to the number |
join | string, array[string] -> string | Returns array elements joined into a string with a separator |
keys | object -> array[string] | Returns an array of keys from an object |
length | array|object|string -> number | Returns the length of an array, object, or string |
map | expression, array -> array | Applies an expression to each element of an array |
max | array[number]|array[string] -> number|string | Returns the maximum value in an array |
max_by | array, expression -> any | Returns the element with maximum value by expression |
merge | object... -> object | Merges objects into a single object |
min | array[number]|array[string] -> number|string | Returns the minimum value in an array |
min_by | array, expression -> any | Returns the element with minimum value by expression |
not_null | any... -> any | Returns the first non-null argument |
reverse | array|string -> array|string | Reverses an array or string |
sort | array[number]|array[string] -> array | Sorts an array of numbers or strings |
sort_by | array, expression -> array | Sorts an array by expression result |
starts_with | string, string -> boolean | Returns true if the subject starts with the prefix |
sum | array[number] -> number | Returns the sum of an array of numbers |
to_array | any -> array | Converts a value to an array |
to_number | any -> number | Converts a value to a number |
to_string | any -> string | Converts a value to a string |
type | any -> string | Returns the type of a value as a string |
values | object -> array | Returns an array of values from an object |
Functions
abs
Returns the absolute value of a number
Signature: number -> number
Examples:
# Negative number
abs(`-5`) -> 5
# Positive number
abs(`5`) -> 5
# Zero
abs(`0`) -> 0
CLI Usage:
echo '{}' | jpx 'abs(`-5`)'
avg
Returns the average of an array of numbers
Signature: array[number] -> number
Examples:
# Simple average
avg([1, 2, 3]) -> 2
# Four numbers
avg([10, 20, 30, 40]) -> 25
# Single element
avg([5]) -> 5
CLI Usage:
echo '{}' | jpx 'avg([1, 2, 3])'
ceil
Returns the smallest integer greater than or equal to the number
Signature: number -> number
Examples:
# Round up decimal
ceil(`1.5`) -> 2
# Negative rounds toward zero
ceil(`-1.5`) -> -1
# Integer unchanged
ceil(`5`) -> 5
CLI Usage:
echo '{}' | jpx 'ceil(`1.5`)'
contains
Returns true if the subject contains the search value
Signature: array|string, any -> boolean
Examples:
# Array contains number
contains([1, 2, 3], `2`) -> true
# String contains substring
contains('hello', 'ell') -> true
# Not found
contains([1, 2, 3], `5`) -> false
CLI Usage:
echo '{}' | jpx 'contains([1, 2, 3], `2`)'
ends_with
Returns true if the subject ends with the suffix
Signature: string, string -> boolean
Examples:
# Ends with suffix
ends_with('hello', 'lo') -> true
# Does not end with
ends_with('hello', 'he') -> false
# File extension
ends_with('test.txt', '.txt') -> true
CLI Usage:
echo '{}' | jpx 'ends_with(`"hello"`, `"lo"`)'
floor
Returns the largest integer less than or equal to the number
Signature: number -> number
Examples:
# Round down decimal
floor(`1.9`) -> 1
# Negative rounds away from zero
floor(`-1.5`) -> -2
# Integer unchanged
floor(`5`) -> 5
CLI Usage:
echo '{}' | jpx 'floor(`1.9`)'
join
Returns array elements joined into a string with a separator
Signature: string, array[string] -> string
Examples:
# Join with comma
join(', ', ['a', 'b', 'c']) -> \"a, b, c\"
# Join date parts
join('-', ['2024', '01', '15']) -> \"2024-01-15\"
# Join without separator
join('', ['a', 'b', 'c']) -> \"abc\"
CLI Usage:
echo '{}' | jpx 'join(`", "`, ['a', `"b"`, 'c'])'
keys
Returns an array of keys from an object
Signature: object -> array[string]
Examples:
# Get object keys
keys({a: 1, b: 2}) -> [\"a\", \"b\"]
# Keys sorted alphabetically
keys({name: \"John\", age: 30}) -> [\"age\", \"name\"]
# Empty object
keys({}) -> []
CLI Usage:
echo '{}' | jpx 'keys({a: 1, b: 2})'
length
Returns the length of an array, object, or string
Signature: array|object|string -> number
Examples:
# Array length
length([1, 2, 3]) -> 3
# String length
length('hello') -> 5
# Object key count
length({a: 1, b: 2}) -> 2
CLI Usage:
echo '{}' | jpx 'length([1, 2, 3])'
map
Applies an expression to each element of an array
Signature: expression, array -> array
Examples:
# Extract field from objects
map(&a, [{a: 1}, {a: 2}]) -> [1, 2]
# Apply function
map(&length(@), ['a', 'bb', 'ccc']) -> [1, 2, 3]
# Identity mapping
map(&@, [1, 2, 3]) -> [1, 2, 3]
CLI Usage:
echo '{}' | jpx 'map(&a, [{a: 1}, {a: 2}])'
max
Returns the maximum value in an array
Signature: array[number]|array[string] -> number|string
Examples:
# Max of numbers
max([1, 3, 2]) -> 3
# Max of strings
max(['a', 'c', 'b']) -> 'c'
# Negative numbers
max([-5, -1, -10]) -> -1
CLI Usage:
echo '{}' | jpx 'max([1, 3, 2])'
max_by
Returns the element with maximum value by expression
Signature: array, expression -> any
Examples:
# Max by field
max_by([{a: 1}, {a: 2}], &a) -> {a: 2}
# Max by age
max_by([{name: 'a', age: 30}, {name: 'b', age: 25}], &age) -> {name: 'a', age: 30}
# Max by length
max_by(['aa', 'b', 'ccc'], &length(@)) -> 'ccc'
CLI Usage:
echo '{}' | jpx 'max_by([{a: 1}, {a: 2}], &a)'
merge
Merges objects into a single object
Signature: object... -> object
Examples:
# Merge two objects
merge({a: 1}, {b: 2}) -> {a: 1, b: 2}
# Later values override
merge({a: 1}, {a: 2}) -> {a: 2}
# Multiple objects
merge({a: 1}, {b: 2}, {c: 3}) -> {a: 1, b: 2, c: 3}
CLI Usage:
echo '{}' | jpx 'merge({a: 1}, {b: 2})'
min
Returns the minimum value in an array
Signature: array[number]|array[string] -> number|string
Examples:
# Min of numbers
min([1, 3, 2]) -> 1
# Min of strings
min(['a', 'c', 'b']) -> 'a'
# Negative numbers
min([-5, -1, -10]) -> -10
CLI Usage:
echo '{}' | jpx 'min([1, 3, 2])'
min_by
Returns the element with minimum value by expression
Signature: array, expression -> any
Examples:
# Min by field
min_by([{a: 1}, {a: 2}], &a) -> {a: 1}
# Min by age
min_by([{name: 'a', age: 30}, {name: 'b', age: 25}], &age) -> {name: 'b', age: 25}
# Min by length
min_by(['aa', 'b', 'ccc'], &length(@)) -> 'b'
CLI Usage:
echo '{}' | jpx 'min_by([{a: 1}, {a: 2}], &a)'
not_null
Returns the first non-null argument
Signature: any... -> any
Examples:
# Skip nulls
not_null(`null`, 'a', 'b') -> \"a\"
# First non-null
not_null('first', 'second') -> \"first\"
# Multiple nulls
not_null(`null`, `null`, `1`) -> 1
CLI Usage:
echo '{}' | jpx 'not_null(`null`, `"a"`, `"b"`)'
reverse
Reverses an array or string
Signature: array|string -> array|string
Examples:
# Reverse array
reverse([1, 2, 3]) -> [3, 2, 1]
# Reverse string
reverse('hello') -> 'olleh'
# Empty array
reverse([]) -> []
CLI Usage:
echo '{}' | jpx 'reverse([1, 2, 3])'
sort
Sorts an array of numbers or strings
Signature: array[number]|array[string] -> array
Examples:
# Sort numbers
sort([3, 1, 2]) -> [1, 2, 3]
# Sort strings
sort(['c', 'a', 'b']) -> ['a', 'b', 'c']
# Empty array
sort([]) -> []
CLI Usage:
echo '{}' | jpx 'sort([3, 1, 2])'
sort_by
Sorts an array by expression result
Signature: array, expression -> array
Examples:
# Sort by field
sort_by([{a: 2}, {a: 1}], &a) -> [{a: 1}, {a: 2}]
# Sort by length
sort_by(['bb', 'a', 'ccc'], &length(@)) -> ['a', 'bb', 'ccc']
# Sort by name
sort_by([{name: 'z'}, {name: 'a'}], &name) -> [{name: 'a'}, {name: 'z'}]
CLI Usage:
echo '{}' | jpx 'sort_by([{a: 2}, {a: 1}], &a)'
starts_with
Returns true if the subject starts with the prefix
Signature: string, string -> boolean
Examples:
# Starts with prefix
starts_with('hello', 'he') -> true
# Does not start with
starts_with('hello', 'lo') -> false
# URL protocol
starts_with('https://example.com', 'https') -> true
CLI Usage:
echo '{}' | jpx 'starts_with(`"hello"`, `"he"`)'
sum
Returns the sum of an array of numbers
Signature: array[number] -> number
Examples:
# Sum of numbers
sum([1, 2, 3]) -> 6
# With negative
sum([10, -5, 3]) -> 8
# Empty array
sum([]) -> 0
CLI Usage:
echo '{}' | jpx 'sum([1, 2, 3])'
to_array
Converts a value to an array
Signature: any -> array
Examples:
# String to array
to_array('hello') -> [\"hello\"]
# Array unchanged
to_array([1, 2]) -> [1, 2]
# Number to array
to_array(`5`) -> [5]
CLI Usage:
echo '{}' | jpx 'to_array(`"hello"`)'
to_number
Converts a value to a number
Signature: any -> number
Examples:
# String to number
to_number('42') -> 42
# String to float
to_number('3.14') -> 3.14
# Number unchanged
to_number(`5`) -> 5
CLI Usage:
echo '{}' | jpx 'to_number(`"42"`)'
to_string
Converts a value to a string
Signature: any -> string
Examples:
# Number to string
to_string(`42`) -> \"42\"
# Boolean to string
to_string(`true`) -> \"true\"
# String unchanged
to_string('hello') -> \"hello\"
CLI Usage:
echo '{}' | jpx 'to_string(`42`)'
type
Returns the type of a value as a string
Signature: any -> string
Examples:
# String type
type('hello') -> \"string\"
# Number type
type(`42`) -> \"number\"
# Array type
type([1, 2]) -> \"array\"
# Object type
type({a: 1}) -> \"object\"
CLI Usage:
echo '{}' | jpx 'type(`"hello"`)'
values
Returns an array of values from an object
Signature: object -> array
Examples:
# Get object values
values({a: 1, b: 2}) -> [1, 2]
# String values
values({x: 'hello', y: 'world'}) -> ['hello', 'world']
# Empty object
values({}) -> []
CLI Usage:
echo '{}' | jpx 'values({a: 1, b: 2})'
String Functions
Functions for string manipulation: case conversion, splitting, joining, padding, and text processing.
Summary
| Function | Signature | Description |
|---|---|---|
abbreviate | string, number, string? -> string | Truncate string with ellipsis suffix |
camel_case | string -> string | Convert to camelCase |
capitalize | string -> string | Capitalize the first character |
center | string, number, string? -> string | Center-pad string to given width |
concat | string... -> string | Concatenate strings |
explode | string -> array | Convert a string to an array of Unicode codepoints |
find_first | string, string -> number | null | Find first occurrence of substring |
find_last | string, string -> number | null | Find last occurrence of substring |
implode | array -> string | Convert an array of Unicode codepoints to a string |
indices | string, string -> array | Find all indices of substring occurrences |
inside | string, string -> boolean | Check if search string is contained in string |
is_blank | string -> boolean | Check if string is empty or whitespace-only |
kebab_case | string -> string | Convert to kebab-case |
lower | string -> string | Convert string to lowercase |
pascal_case | string -> string | Convert to PascalCase |
ltrimstr | string, string -> string | Remove prefix from string if present |
mask | string, number?, string? -> string | Mask string, keeping last N characters visible |
normalize_whitespace | string -> string | Collapse multiple whitespace to single space |
pad_left | string, number, string -> string | Pad string on the left to reach target length |
pad_right | string, number, string -> string | Pad string on the right to reach target length |
redact | string, string, string? -> string | Redact regex pattern matches with replacement |
repeat | string, number -> string | Repeat a string n times |
shouty_kebab_case | string -> string | Convert to SHOUTY-KEBAB-CASE |
shouty_snake_case | string -> string | Convert to SHOUTY_SNAKE_CASE |
replace | string, string, string -> string | Replace occurrences of a substring |
reverse_string | string -> string | Reverse a string |
rtrimstr | string, string -> string | Remove suffix from string if present |
shell_escape | string -> string | Escape a string for safe use in shell commands (POSIX sh compatible, jq parity) |
slice | string, number, number -> string | Extract substring by start and end index |
snake_case | string -> string | Convert to snake_case |
split | string, string -> array | Split string by delimiter |
sprintf | string, any... -> string | Printf-style string formatting |
substr | string, number, number -> string | Extract substring by start index and length |
title | string -> string | Convert to title case |
train_case | string -> string | Convert to Train-Case |
trim | string -> string | Remove leading and trailing whitespace |
trim_left | string -> string | Remove leading whitespace |
trim_right | string -> string | Remove trailing whitespace |
upper | string -> string | Convert string to uppercase |
wrap | string, number -> string | Wrap text to specified width |
Functions
abbreviate
Truncate string with ellipsis suffix
Signature: string, number, string? -> string
Examples:
# Default ellipsis
abbreviate('hello world', `8`) -> \"hello...\"
# No truncation needed
abbreviate('hello', `10`) -> \"hello\"
# Custom suffix
abbreviate('hello world', `8`, '>>') -> \"hello >>\"
CLI Usage:
echo '{}' | jpx 'abbreviate(`"hello world"`, `8`)'
camel_case
Convert to camelCase
Signature: string -> string
Examples:
# From snake_case
camel_case('hello_world') -> \"helloWorld\"
# From kebab-case
camel_case('hello-world') -> \"helloWorld\"
# From title case
camel_case('Hello World') -> \"helloWorld\"
CLI Usage:
echo '{}' | jpx 'camel_case(`"hello_world"`)'
capitalize
Capitalize the first character
Signature: string -> string
Examples:
# Basic capitalize
capitalize('hello') -> \"Hello\"
# Already uppercase
capitalize('HELLO') -> \"HELLO\"
# Empty string
capitalize('') -> \"\"
CLI Usage:
echo '{}' | jpx 'capitalize(`"hello"`)'
center
Center-pad string to given width
Signature: string, number, string? -> string
Examples:
# Center with spaces
center('hi', `6`) -> \" hi \"
# Center with dashes
center('hi', `6`, '-') -> \"--hi--\"
# Already wider
center('hello', `3`) -> \"hello\"
CLI Usage:
echo '{}' | jpx 'center(`"hi"`, `6`)'
concat
Concatenate strings
Signature: string... -> string
Examples:
# Multiple strings
concat('hello', ' ', 'world') -> \"hello world\"
# Two strings
concat('a', 'b') -> \"ab\"
# Single string
concat('only') -> \"only\"
CLI Usage:
echo '{}' | jpx 'concat(`"hello"`, `" "`, `"world"`)'
explode
Convert a string to an array of Unicode codepoints
Signature: string -> array
Examples:
# ASCII characters
explode('abc') -> [97, 98, 99]
# Unicode characters
explode('A☺') -> [65, 9786]
# Empty string
explode('') -> []
CLI Usage:
echo '{}' | jpx 'explode(`"abc"`)'
find_first
Find first occurrence of substring
Signature: string, string -> number | null
JEP: JEP-014
Examples:
# Find character
find_first('hello', 'l') -> 2
# Find substring
find_first('hello world', 'world') -> 6
# Not found
find_first('hello', 'x') -> null
CLI Usage:
echo '{}' | jpx 'find_first(`"hello"`, `"l"`)'
find_last
Find last occurrence of substring
Signature: string, string -> number | null
JEP: JEP-014
Examples:
# Find last character
find_last('hello', 'l') -> 3
# Find last substring
find_last('foo bar foo', 'foo') -> 8
# Not found
find_last('hello', 'x') -> null
CLI Usage:
echo '{}' | jpx 'find_last(`"hello"`, `"l"`)'
implode
Convert an array of Unicode codepoints to a string
Signature: array -> string
Examples:
# ASCII codepoints
implode([97, 98, 99]) -> \"abc\"
# Unicode codepoints
implode([65, 9786]) -> \"A☺\"
# Empty array
implode([]) -> \"\"
CLI Usage:
echo '{}' | jpx 'implode([97, 98, 99])'
indices
Find all indices of substring occurrences
Signature: string, string -> array
Examples:
# Multiple occurrences
indices('hello', 'l') -> [2, 3]
# Overlapping matches
indices('ababa', 'aba') -> [0, 2]
# No matches
indices('hello', 'x') -> []
CLI Usage:
echo '{}' | jpx 'indices(`"hello"`, `"l"`)'
inside
Check if search string is contained in string
Signature: string, string -> boolean
Examples:
# Found
inside('world', 'hello world') -> true
# Not found
inside('foo', 'hello world') -> false
# Empty string always matches
inside('', 'hello') -> true
CLI Usage:
echo '{}' | jpx 'inside(`"world"`, `"hello world"`)'
is_blank
Check if string is empty or whitespace-only
Signature: string -> boolean
Examples:
# Whitespace only
is_blank(' ') -> true
# Empty string
is_blank('') -> true
# Has content
is_blank('hello') -> false
CLI Usage:
echo '{}' | jpx 'is_blank(`" "`)'
kebab_case
Convert to kebab-case
Signature: string -> string
Examples:
# From camelCase
kebab_case('helloWorld') -> \"hello-world\"
# From snake_case
kebab_case('hello_world') -> \"hello-world\"
# From title case
kebab_case('Hello World') -> \"hello-world\"
CLI Usage:
echo '{}' | jpx 'kebab_case(`"helloWorld"`)'
lower
Convert string to lowercase
Signature: string -> string
JEP: JEP-014
Examples:
# All uppercase
lower('HELLO') -> \"hello\"
# Mixed case
lower('Hello World') -> \"hello world\"
# Already lowercase
lower('hello') -> \"hello\"
CLI Usage:
echo '{}' | jpx 'lower(`"HELLO"`)'
pascal_case
Convert to PascalCase (also known as UpperCamelCase)
Signature: string -> string
Examples:
# From snake_case
pascal_case('hello_world') -> "HelloWorld"
# From camelCase
pascal_case('helloWorld') -> "HelloWorld"
# From kebab-case
pascal_case('hello-world') -> "HelloWorld"
# Handles acronyms
pascal_case('xml_parser') -> "XmlParser"
CLI Usage:
echo '{}' | jpx 'pascal_case(`"hello_world"`)'
ltrimstr
Remove prefix from string if present
Signature: string, string -> string
Examples:
# Remove prefix
ltrimstr('foobar', 'foo') -> \"bar\"
# Prefix not found
ltrimstr('foobar', 'bar') -> \"foobar\"
# Empty prefix
ltrimstr('hello', '') -> \"hello\"
CLI Usage:
echo '{}' | jpx 'ltrimstr(`"foobar"`, `"foo"`)'
mask
Mask string, keeping last N characters visible
Signature: string, number?, string? -> string
Examples:
# Credit card
mask('4111111111111111', `4`) -> \"************1111\"
# Mask all
mask('secret', `0`) -> \"******\"
# Custom mask char
mask('password', `4`, '#') -> \"####word\"
CLI Usage:
echo '{}' | jpx 'mask(`"4111111111111111"`, `4`)'
normalize_whitespace
Collapse multiple whitespace to single space
Signature: string -> string
Examples:
# Multiple spaces
normalize_whitespace('a b c') -> \"a b c\"
# Newlines to space
normalize_whitespace('a\\n\\nb') -> \"a b\"
# Already normalized
normalize_whitespace('hello') -> \"hello\"
CLI Usage:
echo '{}' | jpx 'normalize_whitespace(`"a b c"`)'
pad_left
Pad string on the left to reach target length
Signature: string, number, string -> string
JEP: JEP-014
Examples:
# Zero-pad number
pad_left('5', `3`, '0') -> \"005\"
# Right-align text
pad_left('hi', `5`, ' ') -> \" hi\"
# Already long enough
pad_left('hello', `3`, '0') -> \"hello\"
CLI Usage:
echo '{}' | jpx 'pad_left(`"5"`, `3`, `"0"`)'
pad_right
Pad string on the right to reach target length
Signature: string, number, string -> string
JEP: JEP-014
Examples:
# Pad with zeros
pad_right('5', `3`, '0') -> \"500\"
# Left-align text
pad_right('hi', `5`, ' ') -> \"hi \"
# Already long enough
pad_right('hello', `3`, '0') -> \"hello\"
CLI Usage:
echo '{}' | jpx 'pad_right(`"5"`, `3`, `"0"`)'
redact
Redact regex pattern matches with replacement
Signature: string, string, string? -> string
Examples:
# Redact email
redact('email: test@example.com', '\\S+@\\S+', '[EMAIL]') -> \"email: [EMAIL]\"
# Redact phone
redact('call 555-1234', '\\d{3}-\\d{4}', '[PHONE]') -> \"call [PHONE]\"
# No matches
redact('no match', 'xyz', '[X]') -> \"no match\"
CLI Usage:
echo '{}' | jpx 'redact(`"email: test@example.com"`, `"\\S+@\\S+"`, `"[EMAIL]"`)'
repeat
Repeat a string n times
Signature: string, number -> string
Examples:
# Repeat 3 times
repeat('ab', `3`) -> \"ababab\"
# Create separator
repeat('-', `5`) -> \"-----\"
# Zero times
repeat('x', `0`) -> \"\"
CLI Usage:
echo '{}' | jpx 'repeat(`"ab"`, `3`)'
shouty_kebab_case
Convert to SHOUTY-KEBAB-CASE (also known as COBOL-CASE or SCREAMING-KEBAB-CASE)
Signature: string -> string
Examples:
# From camelCase
shouty_kebab_case('helloWorld') -> "HELLO-WORLD"
# From snake_case
shouty_kebab_case('hello_world') -> "HELLO-WORLD"
# From PascalCase
shouty_kebab_case('HelloWorld') -> "HELLO-WORLD"
CLI Usage:
echo '{}' | jpx 'shouty_kebab_case(`"helloWorld"`)'
shouty_snake_case
Convert to SHOUTY_SNAKE_CASE (also known as SCREAMING_SNAKE_CASE or CONSTANT_CASE)
Signature: string -> string
Examples:
# From camelCase
shouty_snake_case('helloWorld') -> "HELLO_WORLD"
# From kebab-case
shouty_snake_case('hello-world') -> "HELLO_WORLD"
# From PascalCase
shouty_snake_case('HelloWorld') -> "HELLO_WORLD"
CLI Usage:
echo '{}' | jpx 'shouty_snake_case(`"helloWorld"`)'
replace
Replace occurrences of a substring
Signature: string, string, string -> string
JEP: JEP-014
Examples:
# Replace all occurrences
replace('hello', 'l', 'L') -> \"heLLo\"
# No match
replace('hello', 'x', 'y') -> \"hello\"
# Replace all
replace('aaa', 'a', 'b') -> \"bbb\"
CLI Usage:
echo '{}' | jpx 'replace(`"hello"`, `"l"`, `"L"`)'
reverse_string
Reverse a string
Signature: string -> string
Examples:
# Reverse word
reverse_string('hello') -> \"olleh\"
# Two chars
reverse_string('ab') -> \"ba\"
# Empty string
reverse_string('') -> \"\"
CLI Usage:
echo '{}' | jpx 'reverse_string(`"hello"`)'
rtrimstr
Remove suffix from string if present
Signature: string, string -> string
Examples:
# Remove suffix
rtrimstr('foobar', 'bar') -> \"foo\"
# Remove file extension
rtrimstr('hello.txt', '.txt') -> \"hello\"
# Suffix not present
rtrimstr('hello', 'xyz') -> \"hello\"
# Only removes once
rtrimstr('barbar', 'bar') -> \"bar\"
CLI Usage:
echo '{}' | jpx 'rtrimstr(`"foobar"`, `"bar"`)'
shell_escape
Escape a string for safe use in shell commands (POSIX sh compatible, jq parity)
Signature: string -> string
Examples:
# Simple string unchanged
shell_escape('hello') -> \"hello\"
# Spaces get quoted
shell_escape('hello world') -> \"'hello world'\"
# Variables are escaped
shell_escape('$PATH') -> \"'$PATH'\"
# Single quotes escaped
shell_escape('it'\\''s') -> \"'it'\\\\''s'\"
CLI Usage:
echo '{}' | jpx 'shell_escape(`"hello"`)'
slice
Extract substring by start and end index
Signature: string, number, number -> string
Examples:
# Middle slice
slice('hello', `1`, `4`) -> \"ell\"
# From start
slice('hello', `0`, `2`) -> \"he\"
# To end
slice('hello', `3`, `5`) -> \"lo\"
# Two characters
slice('abcdef', `2`, `4`) -> \"cd\"
CLI Usage:
echo '{}' | jpx 'slice(`"hello"`, `1`, `4`)'
snake_case
Convert to snake_case
Signature: string -> string
Examples:
# From camelCase
snake_case('helloWorld') -> \"hello_world\"
# From PascalCase
snake_case('HelloWorld') -> \"hello_world\"
# From kebab-case
snake_case('hello-world') -> \"hello_world\"
# From UPPER_CASE
snake_case('HELLO_WORLD') -> \"hello_world\"
CLI Usage:
echo '{}' | jpx 'snake_case(`"helloWorld"`)'
split
Split string by delimiter
Signature: string, string -> array
JEP: JEP-014
Examples:
# Basic split
split('a,b,c', ',') -> [\"a\", \"b\", \"c\"]
# Split by space
split('hello world', ' ') -> [\"hello\", \"world\"]
# No delimiter found
split('no-delim', ',') -> [\"no-delim\"]
# Empty string
split('', ',') -> [\"\"]
CLI Usage:
echo '{}' | jpx 'split(`"a,b,c"`, `","`)'
sprintf
Printf-style string formatting
Signature: string, any... -> string
Examples:
# Float formatting
sprintf('Pi is %.2f', `3.14159`) -> \"Pi is 3.14\"
# String interpolation
sprintf('Hello %s', 'world') -> \"Hello world\"
# Integer formatting
sprintf('%d items', `42`) -> \"42 items\"
# Multiple args
sprintf('%s: %d', 'count', `5`) -> \"count: 5\"
CLI Usage:
echo '{}' | jpx 'sprintf(`"Pi is %.2f"`, `3.14159`)'
substr
Extract substring by start index and length
Signature: string, number, number -> string
Examples:
# From index 1, length 3
substr('hello', `1`, `3`) -> \"ell\"
# From start
substr('hello', `0`, `2`) -> \"he\"
# Middle portion
substr('abcdef', `2`, `2`) -> \"cd\"
# Single character
substr('hello', `4`, `1`) -> \"o\"
CLI Usage:
echo '{}' | jpx 'substr(`"hello"`, `1`, `3`)'
title
Convert to title case
Signature: string -> string
Examples:
# Basic title case
title('hello world') -> \"Hello World\"
# From uppercase
title('HELLO WORLD') -> \"Hello World\"
# Single word
title('hello') -> \"Hello\"
# Single letters
title('a b c') -> \"A B C\"
CLI Usage:
echo '{}' | jpx 'title(`"hello world"`)'
train_case
Convert to Train-Case (also known as HTTP-Header-Case)
Signature: string -> string
Examples:
# From camelCase
train_case('helloWorld') -> "Hello-World"
# From snake_case
train_case('hello_world') -> "Hello-World"
# From lowercase
train_case('content type') -> "Content-Type"
CLI Usage:
echo '{}' | jpx 'train_case(`"helloWorld"`)'
trim
Remove leading and trailing whitespace
Signature: string -> string
JEP: JEP-014
Examples:
# Remove both sides
trim(' hello ') -> \"hello\"
# No whitespace
trim('hello') -> \"hello\"
# Only whitespace
trim(' ') -> \"\"
# Tabs and newlines
trim('\t\nhello\t\n') -> \"hello\"
CLI Usage:
echo '{}' | jpx 'trim(`" hello "`)'
trim_left
Remove leading whitespace
Signature: string -> string
JEP: JEP-014
Examples:
# Remove leading spaces
trim_left(' hello') -> \"hello\"
# Trailing preserved
trim_left('hello ') -> \"hello \"
# Remove tabs
trim_left('\t\thello') -> \"hello\"
# No change needed
trim_left('hello') -> \"hello\"
CLI Usage:
echo '{}' | jpx 'trim_left(`" hello"`)'
trim_right
Remove trailing whitespace
Signature: string -> string
JEP: JEP-014
Examples:
# Remove trailing spaces
trim_right('hello ') -> \"hello\"
# Leading preserved
trim_right(' hello') -> \" hello\"
# Remove tabs
trim_right('hello\t\t') -> \"hello\"
# No change needed
trim_right('hello') -> \"hello\"
CLI Usage:
echo '{}' | jpx 'trim_right(`"hello "`)'
upper
Convert string to uppercase
Signature: string -> string
JEP: JEP-014
Examples:
# Basic uppercase
upper('hello') -> \"HELLO\"
# Mixed case
upper('Hello World') -> \"HELLO WORLD\"
# Already uppercase
upper('HELLO') -> \"HELLO\"
# With numbers
upper('abc123') -> \"ABC123\"
CLI Usage:
echo '{}' | jpx 'upper(`"hello"`)'
wrap
Wrap text to specified width
Signature: string, number -> string
Examples:
# Wrap at word boundary
wrap('hello world', `5`) -> \"hello\\nworld\"
# Multiple wraps
wrap('a b c d e', `3`) -> \"a b\\nc d\\ne\"
# No wrap needed
wrap('short', `10`) -> \"short\"
# Longer text
wrap('one two three', `7`) -> \"one two\\nthree\"
CLI Usage:
echo '{}' | jpx 'wrap(`"hello world"`, `5`)'
Array Functions
Functions for working with arrays: chunking, filtering, transforming, and combining array data.
Summary
| Function | Signature | Description |
|---|---|---|
bsearch | array, any -> number | Binary search in a sorted array, returns index or negative insertion point (jq parity) |
cartesian | array, array? -> array | Compute cartesian product of arrays (jq parity for N-way product) |
chunk | array, number -> array | Split array into chunks of size n |
compact | array -> array | Remove null values from array |
difference | array, array -> array | Elements in first array not in second |
drop | array, number -> array | Drop first n elements |
find_index | array, any -> number | null | Find index of value in array |
first | array -> any | Get first element of array |
flatten | array -> array | Flatten array one level deep |
flatten_deep | array -> array | Recursively flatten nested arrays |
frequencies | array -> object | Count occurrences of each value |
group_by | array, string -> object | Group array elements by key |
includes | array, any -> boolean | Check if array contains value |
index_at | array, number -> any | Get element at index (supports negative) |
index_by | array, string -> object | Create lookup map from array using key field (last value wins for duplicates) |
indices_array | array, any -> array | Find all indices where a value appears in an array (jq parity) |
inside_array | array, array -> boolean | Check if all elements of first array are contained in second array (inverse of contains, jq parity) |
intersection | array, array -> array | Elements common to both arrays |
last | array -> any | Get last element of array |
pairwise | array -> array | Return adjacent pairs from array |
range | number, number -> array | Generate array of numbers |
sliding_window | array, number -> array | Create overlapping windows of size n (alias for window) |
take | array, number -> array | Take first n elements |
transpose | array -> array | Transpose a 2D array (swap rows and columns) |
union | array, array -> array | Unique elements from both arrays |
unique | array -> array | Remove duplicate values |
zip | array, array -> array | Zip two arrays together |
Functions
bsearch
Binary search in a sorted array, returns index or negative insertion point (jq parity)
Signature: array, any -> number
Examples:
# Found at index 2
bsearch([1, 3, 5, 7, 9], `5`) -> 2
# Not found, would insert at index 2
bsearch([1, 3, 5, 7, 9], `4`) -> -3
# Not found, would insert at index 0
bsearch([1, 3, 5, 7, 9], `0`) -> -1
CLI Usage:
echo '{}' | jpx 'bsearch([1, 3, 5, 7, 9], `5`)'
cartesian
Compute cartesian product of arrays (jq parity for N-way product)
Signature: array, array? -> array
Examples:
# Two-array product
cartesian([1, 2], [3, 4]) -> [[1, 3], [1, 4], [2, 3], [2, 4]]
# N-way product (jq style)
cartesian([[1, 2], [3, 4]]) -> [[1, 3], [1, 4], [2, 3], [2, 4]]
# Three-way product
cartesian([['a', 'b'], [1, 2], ['x', 'y']]) -> [['a', 1, 'x'], ['a', 1, 'y'], ...]
# Single array
cartesian([[1, 2]]) -> [[1], [2]]
# Empty array produces empty result
cartesian([[], [1, 2]]) -> []
CLI Usage:
echo '{}' | jpx 'cartesian([1, 2], [3, 4])'
chunk
Split array into chunks of size n
Signature: array, number -> array
Examples:
# Basic chunking
chunk([1, 2, 3, 4], `2`) -> [[1, 2], [3, 4]]
# Uneven chunks
chunk([1, 2, 3, 4, 5], `2`) -> [[1, 2], [3, 4], [5]]
# Empty array
chunk([], `2`) -> []
CLI Usage:
echo '{}' | jpx 'chunk([1, 2, 3, 4], `2`)'
compact
Remove null values from array
Signature: array -> array
Examples:
# Remove nulls
compact([1, null, 2, null]) -> [1, 2]
# All nulls
compact([null, null]) -> []
# No nulls
compact([1, 2, 3]) -> [1, 2, 3]
CLI Usage:
echo '{}' | jpx 'compact([1, null, 2, null])'
difference
Elements in first array not in second
Signature: array, array -> array
Examples:
# Remove matching elements
difference([1, 2, 3], [2]) -> [1, 3]
# No overlap
difference([1, 2, 3], [4, 5]) -> [1, 2, 3]
# All removed
difference([1, 2], [1, 2]) -> []
CLI Usage:
echo '{}' | jpx 'difference([1, 2, 3], [2])'
drop
Drop first n elements
Signature: array, number -> array
Examples:
# Drop first 2
drop([1, 2, 3, 4], `2`) -> [3, 4]
# Drop none
drop([1, 2, 3], `0`) -> [1, 2, 3]
# Drop more than length
drop([1, 2], `5`) -> []
CLI Usage:
echo '{}' | jpx 'drop([1, 2, 3, 4], `2`)'
find_index
Find index of value in array
Signature: array, any -> number | null
Examples:
# Find existing value
find_index([1, 2, 3], `2`) -> 1
# Find string
find_index(['a', 'b', 'c'], 'b') -> 1
# Value not found
find_index([1, 2, 3], `99`) -> null
CLI Usage:
echo '{}' | jpx 'find_index([1, 2, 3], `2`)'
first
Get first element of array
Signature: array -> any
Examples:
# Get first number
first([1, 2, 3]) -> 1
# Get first string
first(['a', 'b']) -> 'a'
# Empty array returns null
first([]) -> null
CLI Usage:
echo '{}' | jpx 'first([1, 2, 3])'
flatten
Flatten array one level deep
Signature: array -> array
Examples:
# Flatten nested arrays
flatten([[1, 2], [3]]) -> [1, 2, 3]
# Only one level deep
flatten([[1, [2]], [3]]) -> [1, [2], 3]
# Already flat
flatten([1, 2, 3]) -> [1, 2, 3]
CLI Usage:
echo '{}' | jpx 'flatten([[1, 2], [3]])'
flatten_deep
Recursively flatten nested arrays
Signature: array -> array
Examples:
# Deeply nested
flatten_deep([[1, [2]], [3]]) -> [1, 2, 3]
# Multiple levels
flatten_deep([[[1]], [[2]], [[3]]]) -> [1, 2, 3]
# Already flat
flatten_deep([1, 2, 3]) -> [1, 2, 3]
CLI Usage:
echo '{}' | jpx 'flatten_deep([[1, [2]], [3]])'
frequencies
Count occurrences of each value
Signature: array -> object
Examples:
# Count strings
frequencies(['a', 'b', 'a']) -> {a: 2, b: 1}
# Count numbers
frequencies([1, 2, 1, 1]) -> {1: 3, 2: 1}
# Empty array
frequencies([]) -> {}
CLI Usage:
echo '{}' | jpx 'frequencies(['a', `"b"`, 'a'])'
group_by
Group array elements by key
Signature: array, string -> object
Examples:
# Group by field
group_by([{t: 'a'}, {t: 'b'}, {t: 'a'}], 't') -> {a: [{t:'a'}, {t:'a'}], b: [{t:'b'}]}
# Group users by role
group_by(users, 'role') -> {admin: [...], user: [...]}
# Empty array returns empty object
group_by([], 'key') -> {}
CLI Usage:
echo '{}' | jpx 'group_by([{t: 'a'}, {t: 'b'}, {t: 'a'}], `"t"`)'
includes
Check if array contains value
Signature: array, any -> boolean
Examples:
# Value found
includes([1, 2, 3], `2`) -> true
# Value not found
includes([1, 2, 3], `99`) -> false
# String value
includes(['a', 'b'], 'a') -> true
CLI Usage:
echo '{}' | jpx 'includes([1, 2, 3], `2`)'
index_at
Get element at index (supports negative)
Signature: array, number -> any
Examples:
# First element
index_at([1, 2, 3], `0`) -> 1
# Last element (negative index)
index_at([1, 2, 3], `-1`) -> 3
# Out of bounds
index_at([1, 2, 3], `99`) -> null
CLI Usage:
echo '{}' | jpx 'index_at([1, 2, 3], `0`)'
index_by
Create lookup map from array using key field (last value wins for duplicates)
Signature: array, string -> object
Examples:
# Index by id field
index_by([{id: 1, name: "alice"}, {id: 2, name: "bob"}], "id") -> {"1": {id: 1, name: "alice"}, "2": {id: 2, name: "bob"}}
# Index by string key
index_by([{code: "US", name: "USA"}], "code") -> {"US": {code: "US", name: "USA"}}
# Last value wins for duplicates
index_by([{t: "a", v: 1}, {t: "a", v: 2}], "t") -> {"a": {t: "a", v: 2}}
# Empty array returns empty object
index_by([], "id") -> {}
CLI Usage:
echo '{}' | jpx 'index_by([{id: 1, name: "alice"}, {id: 2, name: "bob"}], "id")'
indices_array
Find all indices where a value appears in an array (jq parity)
Signature: array, any -> array
Examples:
# Find all occurrences
indices_array([1, 2, 3, 2, 4, 2], `2`) -> [1, 3, 5]
# String values
indices_array(['a', 'b', 'a', 'c'], `'a'`) -> [0, 2]
# Not found returns empty
indices_array([1, 2, 3], `5`) -> []
CLI Usage:
echo '{}' | jpx 'indices_array([1, 2, 3, 2, 4, 2], `2`)'
inside_array
Check if all elements of first array are contained in second array (inverse of contains, jq parity)
Signature: array, array -> boolean
Examples:
# Subset check
inside_array([1, 2], [1, 2, 3, 4]) -> true
# Not a subset
inside_array([1, 5], [1, 2, 3, 4]) -> false
# Empty is subset of any
inside_array([], [1, 2, 3]) -> true
CLI Usage:
echo '{}' | jpx 'inside_array([1, 2], [1, 2, 3, 4])'
intersection
Elements common to both arrays
Signature: array, array -> array
Examples:
# Common elements
intersection([1, 2], [2, 3]) -> [2]
# No overlap
intersection([1, 2], [3, 4]) -> []
# Identical arrays
intersection([1, 2, 3], [1, 2, 3]) -> [1, 2, 3]
CLI Usage:
echo '{}' | jpx 'intersection([1, 2], [2, 3])'
last
Get last element of array
Signature: array -> any
Examples:
# Get last number
last([1, 2, 3]) -> 3
# Get last string
last(['a', 'b']) -> 'b'
# Empty array returns null
last([]) -> null
CLI Usage:
echo '{}' | jpx 'last([1, 2, 3])'
pairwise
Return adjacent pairs from array
Signature: array -> array
Examples:
# Adjacent pairs
pairwise([1, 2, 3]) -> [[1, 2], [2, 3]]
# Single pair
pairwise([1, 2]) -> [[1, 2]]
# Too few elements
pairwise([1]) -> []
CLI Usage:
echo '{}' | jpx 'pairwise([1, 2, 3])'
range
Generate array of numbers
Signature: number, number -> array
Examples:
# Range 1 to 4
range(`1`, `5`) -> [1, 2, 3, 4]
# Range from zero
range(`0`, `3`) -> [0, 1, 2]
# Empty range
range(`5`, `5`) -> []
CLI Usage:
echo '{}' | jpx 'range(`1`, `5`)'
sliding_window
Create overlapping windows of size n (alias for window)
Signature: array, number -> array
Aliases: window
Examples:
# Size 2 windows
sliding_window([1, 2, 3, 4], `2`) -> [[1, 2], [2, 3], [3, 4]]
# Size 3 windows
sliding_window([1, 2, 3, 4], `3`) -> [[1, 2, 3], [2, 3, 4]]
# Window larger than array
sliding_window([1, 2], `3`) -> []
CLI Usage:
echo '{}' | jpx 'sliding_window([1, 2, 3, 4], `2`)'
take
Take first n elements
Signature: array, number -> array
Examples:
# Take first 2
take([1, 2, 3, 4], `2`) -> [1, 2]
# Take none
take([1, 2, 3], `0`) -> []
# Take more than length
take([1, 2], `5`) -> [1, 2]
CLI Usage:
echo '{}' | jpx 'take([1, 2, 3, 4], `2`)'
transpose
Transpose a 2D array (swap rows and columns)
Signature: array -> array
Examples:
# Swap rows/columns
transpose([[1, 2], [3, 4]]) -> [[1, 3], [2, 4]]
# 2x3 to 3x2
transpose([[1, 2, 3], [4, 5, 6]]) -> [[1, 4], [2, 5], [3, 6]]
# Empty array
transpose([]) -> []
CLI Usage:
echo '{}' | jpx 'transpose([[1, 2], [3, 4]])'
union
Unique elements from both arrays
Signature: array, array -> array
Examples:
# Combine with dedup
union([1, 2], [2, 3]) -> [1, 2, 3]
# No overlap
union([1, 2], [3, 4]) -> [1, 2, 3, 4]
# Empty first array
union([], [1, 2]) -> [1, 2]
CLI Usage:
echo '{}' | jpx 'union([1, 2], [2, 3])'
unique
Remove duplicate values
Signature: array -> array
Examples:
# Basic deduplication
unique([1, 2, 1, 3]) -> [1, 2, 3]
# String values
unique(['a', 'b', 'a']) -> ['a', 'b']
# Empty array
unique([]) -> []
CLI Usage:
echo '{}' | jpx 'unique([1, 2, 1, 3])'
zip
Zip two arrays together
Signature: array, array -> array
JEP: JEP-013
Examples:
# Basic zip
zip([1, 2], ['a', 'b']) -> [[1, 'a'], [2, 'b']]
# Unequal lengths (truncates)
zip([1, 2, 3], ['a', 'b']) -> [[1, 'a'], [2, 'b']]
# Empty arrays
zip([], []) -> []
CLI Usage:
echo '{}' | jpx 'zip([1, 2], ['a', 'b'])'
Object Functions
Functions for working with JSON objects: merging, filtering keys/values, and transformations.
Summary
| Function | Signature | Description |
|---|---|---|
camel_keys | any -> any | Recursively convert all keys to camelCase |
chunk_by_size | array, number -> array | Split an array into chunks of approximately the specified byte size |
compact_deep | array -> array | Recursively compact arrays, removing nulls at all levels |
completeness | object -> number | Calculate percentage of non-null fields (0-100) |
data_quality_score | any -> object | Analyze data quality and return score with detailed issues |
deep_diff | object, object -> object | Structural diff between two objects |
deep_equals | any, any -> boolean | Deep equality check for any two values |
deep_merge | object, object -> object | Recursively merge objects |
defaults | object, object -> object | Assign default values for missing keys (shallow) |
defaults_deep | object, object -> object | Recursively assign default values for missing keys |
delete_path | any, string -> any | Delete value at JSON pointer path (immutable) |
estimate_size | any -> number | Estimate the JSON serialization size in bytes |
flatten | object -> object | Alias for flatten_keys - flatten nested object with dot notation keys |
flatten_array | any, string? -> object | Flatten nested objects and arrays with dot notation keys (arrays use numeric indices) |
flatten_keys | object -> object | Flatten nested object with dot notation keys |
from_items | array -> object | Convert array of [key, value] pairs to object |
get | any, string, any? -> any | Get value at dot-separated path with optional default |
has | any, string -> boolean | Check if dot-separated path exists |
has_same_shape | any, any -> boolean | Check if two values have the same structure (ignoring actual values) |
infer_schema | any -> object | Infer a JSON Schema-like type description from a value |
invert | object -> object | Swap keys and values |
items | object -> array | Convert object to array of [key, value] pairs |
kebab_keys | any -> any | Recursively convert all keys to kebab-case |
leaves | any -> array | Get all leaf values (non-object, non-array) |
pascal_keys | any -> any | Recursively convert all keys to PascalCase |
leaves_with_paths | any -> array | Get all leaf values with their JSON pointer paths |
mask | string, number? -> string | Mask a string, showing only the last N characters |
omit | object, array -> object | Remove specific keys from object |
paginate | array, number, number -> object | Get a page of items from an array with metadata |
paths | any -> array | List all JSON pointer paths in value |
paths_to | any, string -> array | Find all dot-notation paths to a key anywhere in structure |
pick | object, array -> object | Select specific keys from object |
pluck_deep | any, string -> array | Find all values for a key anywhere in nested structure |
redact | any, array -> any | Recursively replace values at specified keys with [REDACTED] |
redact_keys | any, string -> any | Recursively redact keys matching a regex pattern |
remove_empty | any -> any | Recursively remove nulls, empty strings, empty arrays, and empty objects |
remove_empty_strings | any -> any | Recursively remove empty string values |
remove_nulls | any -> any | Recursively remove null values |
rename_keys | object, object -> object | Rename object keys |
set_path | any, string, any -> any | Set value at JSON pointer path (immutable) |
shouty_kebab_keys | any -> any | Recursively convert all keys to SHOUTY-KEBAB-CASE |
shouty_snake_keys | any -> any | Recursively convert all keys to SHOUTY_SNAKE_CASE |
snake_keys | any -> any | Recursively convert all keys to snake_case |
train_keys | any -> any | Recursively convert all keys to Train-Case |
structural_diff | any, any -> object | Compare two values and return their structural differences |
template | object, string -> string | Expand a template string with values from an object using {{key}} syntax |
template_strict | object, string -> string | null | Expand a template string, returning null if any variable is missing |
truncate_to_size | array, number -> array | Truncate an array to fit within approximately the specified byte size |
type_consistency | array -> object | Check if array elements have consistent types |
unflatten | object -> object | Alias for unflatten_keys - restore nested object from dot notation keys |
unflatten_keys | object -> object | Restore nested object from dot notation keys |
with_entries | object, string -> object | Transform object entries using an expression (jq parity) |
Functions
camel_keys
Recursively convert all keys to camelCase
Signature: any -> any
Examples:
# Snake to camel
camel_keys({user_name: "alice"}) -> {userName: "alice"}
# Kebab to camel
camel_keys({"user-name": "bob"}) -> {userName: "bob"}
# Nested
camel_keys({user_info: {first_name: "x"}}) -> {userInfo: {firstName: "x"}}
CLI Usage:
echo '{}' | jpx 'camel_keys({user_name: "alice"})'
chunk_by_size
Split an array into chunks of approximately the specified byte size
Signature: array, number -> array
Examples:
# All fit in one chunk
chunk_by_size([{a:1}, {b:2}, {c:3}], `100`) -> [[{a:1}, {b:2}, {c:3}]]
# Split into chunks
chunk_by_size([{a:1}, {b:2}], `10`) -> [[{a:1}], [{b:2}]]
CLI Usage:
echo '{}' | jpx 'chunk_by_size([{a:1}, {b:2}, {c:3}], `100`)'
compact_deep
Recursively compact arrays, removing nulls at all levels
Signature: array -> array
Examples:
# Remove nulls from nested arrays
compact_deep([[1, null], [null, 2]]) -> [[1], [2]]
# Deep nesting
compact_deep([[1, null], [null, [2, null]]]) -> [[1], [[2]]]
# Simple array
compact_deep([1, null, 3]) -> [1, 3]
CLI Usage:
echo '{}' | jpx 'compact_deep([[1, null], [null, 2]])'
completeness
Calculate percentage of non-null fields (0-100)
Signature: object -> number
Examples:
# All fields filled
completeness({a: 1, b: 2, c: 3}) -> 100
# One of three filled
completeness({a: 1, b: null, c: null}) -> 33.33
# Nested null counted
completeness({a: 1, b: {c: null}}) -> 66.67
# Empty object is complete
completeness({}) -> 100
CLI Usage:
echo '{}' | jpx 'completeness({a: 1, b: 2, c: 3})'
data_quality_score
Analyze data quality and return score with detailed issues
Signature: any -> object
Examples:
# Perfect data
data_quality_score({a: 1, b: 'hello'}).score -> 100
# Issues detected
data_quality_score({a: null, b: ''}).issues -> [{path: 'a', issue: 'null'}, ...]
# Count null fields
data_quality_score(users).null_count -> 5
# Count type mismatches
data_quality_score(data).type_inconsistencies -> 2
CLI Usage:
echo '{}' | jpx 'data_quality_score({a: 1, b: 'hello'}).score'
deep_diff
Structural diff between two objects
Signature: object, object -> object
Examples:
# Changed value
deep_diff({a: 1}, {a: 2}) -> {added: {}, removed: {}, changed: {a: {from: 1, to: 2}}}
# Added and removed
deep_diff({a: 1}, {b: 2}) -> {added: {b: 2}, removed: {a: 1}, changed: {}}
# No differences
deep_diff({a: 1}, {a: 1}) -> {added: {}, removed: {}, changed: {}}
# New key added
deep_diff({}, {a: 1}) -> {added: {a: 1}, removed: {}, changed: {}}
CLI Usage:
echo '{}' | jpx 'deep_diff({a: 1}, {a: 2})'
deep_equals
Deep equality check for any two values
Signature: any, any -> boolean
Examples:
# Equal nested objects
deep_equals({a: {b: 1}}, {a: {b: 1}}) -> true
# Different values
deep_equals({a: 1}, {a: 2}) -> false
# Equal arrays
deep_equals([1, 2], [1, 2]) -> true
# Order matters
deep_equals([1, 2], [2, 1]) -> false
CLI Usage:
echo '{}' | jpx 'deep_equals({a: {b: 1}}, {a: {b: 1}})'
deep_merge
Recursively merge objects
Signature: object, object -> object
Examples:
# Merge nested objects
deep_merge({a: {b: 1}}, {a: {c: 2}}) -> {a: {b: 1, c: 2}}
# Merge flat objects
deep_merge({a: 1}, {b: 2}) -> {a: 1, b: 2}
# Later values override
deep_merge({a: 1}, {a: 2}) -> {a: 2}
# Merge with empty object
deep_merge({}, {a: 1}) -> {a: 1}
CLI Usage:
echo '{}' | jpx 'deep_merge({a: {b: 1}}, {a: {c: 2}})'
defaults
Assign default values for missing keys (shallow)
Signature: object, object -> object
Examples:
# Keep existing, add missing
defaults({a: 1}, {a: 2, b: 3}) -> {a: 1, b: 3}
# All defaults applied
defaults({}, {a: 1, b: 2}) -> {a: 1, b: 2}
# No defaults needed
defaults({a: 1, b: 2}, {}) -> {a: 1, b: 2}
# Different keys
defaults({x: 1}, {y: 2}) -> {x: 1, y: 2}
CLI Usage:
echo '{}' | jpx 'defaults({a: 1}, {a: 2, b: 3})'
defaults_deep
Recursively assign default values for missing keys
Signature: object, object -> object
Examples:
# Merge nested defaults
defaults_deep({a: {b: 1}}, {a: {c: 2}}) -> {a: {b: 1, c: 2}}
# Keep existing nested
defaults_deep({a: {b: 1}}, {a: {b: 2}}) -> {a: {b: 1}}
# Apply all nested defaults
defaults_deep({}, {a: {b: 1}}) -> {a: {b: 1}}
# Different structure
defaults_deep({x: 1}, {y: {z: 2}}) -> {x: 1, y: {z: 2}}
CLI Usage:
echo '{}' | jpx 'defaults_deep({a: {b: 1}}, {a: {c: 2}})'
delete_path
Delete value at JSON pointer path (immutable)
Signature: any, string -> any
Examples:
# Delete top-level key
delete_path({a: 1, b: 2}, '/b') -> {a: 1}
# Delete nested key
delete_path({a: {b: 1, c: 2}}, '/a/b') -> {a: {c: 2}}
# Delete array element
delete_path([1, 2, 3], '/1') -> [1, 3]
# Path not found
delete_path({a: 1}, '/x') -> {a: 1}
CLI Usage:
echo '{}' | jpx 'delete_path({a: 1, b: 2}, `"/b"`)'
estimate_size
Estimate the JSON serialization size in bytes
Signature: any -> number
Examples:
# String with quotes
estimate_size(`"hello"`) -> 7
# Simple object
estimate_size({a: 1}) -> 7
# Simple array
estimate_size([1, 2, 3]) -> 7
CLI Usage:
echo '{}' | jpx 'estimate_size(`"hello"`)'
flatten
Alias for flatten_keys - flatten nested object with dot notation keys
Signature: object -> object
Examples:
# Simple nested
flatten({a: {b: 1}}) -> {\"a.b\": 1}
# Deep nested
flatten({a: {b: {c: 1}}}) -> {\"a.b.c\": 1}
# Flat object
flatten({a: 1, b: 2}) -> {\"a\": 1, \"b\": 2}
CLI Usage:
echo '{}' | jpx 'flatten({a: {b: 1}})'
flatten_array
Flatten nested objects and arrays with dot notation keys (arrays use numeric indices)
Signature: any, string? -> object
Examples:
# Array with indices
flatten_array({a: [1, 2]}) -> {\"a.0\": 1, \"a.1\": 2}
# Nested object with array
flatten_array({a: {b: [1, 2]}}) -> {\"a.b.0\": 1, \"a.b.1\": 2}
# Array of objects
flatten_array([{a: 1}, {b: 2}]) -> {\"0.a\": 1, \"1.b\": 2}
# Custom separator
flatten_array({a: {b: 1}}, '/') -> {\"a/b\": 1}
CLI Usage:
echo '{}' | jpx 'flatten_array({a: [1, 2]})'
flatten_keys
Flatten nested object with dot notation keys
Signature: object -> object
Examples:
# Simple nested
flatten_keys({a: {b: 1}}) -> {\"a.b\": 1}
# Deep nested
flatten_keys({a: {b: {c: 1}}}) -> {\"a.b.c\": 1}
# Flat object
flatten_keys({a: 1, b: 2}) -> {\"a\": 1, \"b\": 2}
# Mixed nesting
flatten_keys({x: {y: 1}, z: 2}) -> {\"x.y\": 1, \"z\": 2}
CLI Usage:
echo '{}' | jpx 'flatten_keys({a: {b: 1}})'
from_items
Convert array of [key, value] pairs to object
Signature: array -> object
JEP: JEP-013
Examples:
# Single pair
from_items([['a', 1]]) -> {a: 1}
# Multiple pairs
from_items([['a', 1], ['b', 2]]) -> {a: 1, b: 2}
# Empty array
from_items([]) -> {}
# String value
from_items([['x', 'hello']]) -> {x: 'hello'}
CLI Usage:
echo '{}' | jpx 'from_items([['a', 1]])'
get
Get value at dot-separated path with optional default
Signature: any, string, any? -> any
Examples:
# Nested path
get({a: {b: 1}}, 'a.b') -> 1
# Top-level key
get({a: 1}, 'a') -> 1
# Missing with default
get({a: 1}, 'x', 'default') -> 'default'
# Deep path
get({a: {b: {c: 3}}}, 'a.b.c') -> 3
CLI Usage:
echo '{}' | jpx 'get({a: {b: 1}}, `"a.b"`)'
has
Check if dot-separated path exists
Signature: any, string -> boolean
Examples:
# Nested path exists
has({a: {b: 1}}, 'a.b') -> true
# Top-level exists
has({a: 1}, 'a') -> true
# Key missing
has({a: 1}, 'b') -> false
# Nested missing
has({a: {b: 1}}, 'a.c') -> false
CLI Usage:
echo '{}' | jpx 'has({a: {b: 1}}, `"a.b"`)'
has_same_shape
Check if two values have the same structure (ignoring actual values)
Signature: any, any -> boolean
Examples:
# Same keys, different values
has_same_shape({a: 1, b: 2}, {a: 99, b: 100}) -> true
# Different keys
has_same_shape({a: 1}, {a: 1, b: 2}) -> false
# Same length arrays
has_same_shape([1, 2], [3, 4]) -> true
CLI Usage:
echo '{}' | jpx 'has_same_shape({a: 1, b: 2}, {a: 99, b: 100})'
infer_schema
Infer a JSON Schema-like type description from a value
Signature: any -> object
Examples:
# Number type
infer_schema(`42`) -> {type: "number"}
# Object schema
infer_schema({name: "alice", age: 30}) -> {type: "object", properties: {name: {type: "string"}, age: {type: "number"}}}
# Array schema
infer_schema([1, 2, 3]) -> {type: "array", items: {type: "number"}}
CLI Usage:
echo '{}' | jpx 'infer_schema(`42`)'
invert
Swap keys and values
Signature: object -> object
Examples:
# Swap key and value
invert({a: 'x'}) -> {x: 'a'}
# Multiple pairs
invert({a: 'b', c: 'd'}) -> {b: 'a', d: 'c'}
# Empty object
invert({}) -> {}
# Real-world example
invert({name: 'id', value: 'data'}) -> {id: 'name', data: 'value'}
CLI Usage:
echo '{}' | jpx 'invert({a: 'x'})'
items
Convert object to array of [key, value] pairs
Signature: object -> array
JEP: JEP-013
Examples:
# Single key
items({a: 1}) -> [[\"a\", 1]]
# Multiple keys
items({a: 1, b: 2}) -> [[\"a\", 1], [\"b\", 2]]
# Empty object
items({}) -> []
# String value
items({x: 'hello'}) -> [[\"x\", \"hello\"]]
CLI Usage:
echo '{}' | jpx 'items({a: 1})'
kebab_keys
Recursively convert all keys to kebab-case
Signature: any -> any
Examples:
# Camel to kebab
kebab_keys({userName: "alice"}) -> {"user-name": "alice"}
# Snake to kebab
kebab_keys({user_name: "bob"}) -> {"user-name": "bob"}
# Nested
kebab_keys({userInfo: {firstName: "x"}}) -> {"user-info": {"first-name": "x"}}
CLI Usage:
echo '{}' | jpx 'kebab_keys({userName: "alice"})'
leaves
Get all leaf values (non-object, non-array)
Signature: any -> array
Examples:
# Mixed structure
leaves({a: 1, b: [2, 3]}) -> [1, 2, 3]
# Nested object
leaves({a: {b: 1}}) -> [1]
# Flat array
leaves([1, 2, 3]) -> [1, 2, 3]
# Flat object
leaves({a: 1, b: 2}) -> [1, 2]
CLI Usage:
echo '{}' | jpx 'leaves({a: 1, b: [2, 3]})'
pascal_keys
Recursively convert all keys to PascalCase
Signature: any -> any
Examples:
# Camel to pascal
pascal_keys({userName: "alice"}) -> {UserName: "alice"}
# Snake to pascal
pascal_keys({user_name: "bob"}) -> {UserName: "bob"}
# Nested
pascal_keys({userInfo: {firstName: "x"}}) -> {UserInfo: {FirstName: "x"}}
CLI Usage:
echo '{}' | jpx 'pascal_keys({user_name: "alice"})'
leaves_with_paths
Get all leaf values with their JSON pointer paths
Signature: any -> array
Examples:
# Single leaf
leaves_with_paths({a: 1}) -> [{path: \"/a\", value: 1}]
# Nested path
leaves_with_paths({a: {b: 1}}) -> [{path: \"/a/b\", value: 1}]
# Array indices
leaves_with_paths([1, 2]) -> [{path: \"/0\", value: 1}, {path: \"/1\", value: 2}]
# Empty object
leaves_with_paths({}) -> []
CLI Usage:
echo '{}' | jpx 'leaves_with_paths({a: 1})'
mask
Mask a string, showing only the last N characters
Signature: string, number? -> string
Examples:
# Credit card default
mask("4111111111111111") -> "************1111"
# Phone with 3 visible
mask("555-123-4567", `3`) -> "*********567"
# Short string masked
mask("abc") -> "***"
CLI Usage:
echo '{}' | jpx 'mask("4111111111111111")'
omit
Remove specific keys from object
Signature: object, array -> object
Examples:
# Remove one key
omit({a: 1, b: 2}, ['a']) -> {b: 2}
# Remove multiple keys
omit({a: 1, b: 2, c: 3}, ['a', 'c']) -> {b: 2}
# Key not present
omit({a: 1}, ['x']) -> {a: 1}
# Empty removal list
omit({a: 1, b: 2}, []) -> {a: 1, b: 2}
CLI Usage:
echo '{}' | jpx 'omit({a: 1, b: 2}, ['a'])'
paginate
Get a page of items from an array with metadata
Signature: array, number, number -> object
Examples:
# First page
paginate([1,2,3,4,5], `2`, `1`) -> {items: [1,2], page: 1, page_size: 2, total_items: 5, total_pages: 3, has_next: true, has_prev: false}
# Last page
paginate([1,2,3,4,5], `2`, `3`) -> {items: [5], page: 3, page_size: 2, total_items: 5, total_pages: 3, has_next: false, has_prev: true}
CLI Usage:
echo '{}' | jpx 'paginate([1,2,3,4,5], `2`, `1`)'
paths
List all JSON pointer paths in value
Signature: any -> array
Examples:
# Nested object
paths({a: {b: 1}}) -> [\"/a\", \"/a/b\"]
# Flat object
paths({a: 1, b: 2}) -> [\"/a\", \"/b\"]
# Array paths
paths([1, 2]) -> [\"/0\", \"/1\"]
# Empty object
paths({}) -> []
CLI Usage:
echo '{}' | jpx 'paths({a: {b: 1}})'
paths_to
Find all dot-notation paths to a key anywhere in structure
Signature: any, string -> array
Examples:
# Find paths to id
paths_to({a: {id: 1}, b: {id: 2}}, "id") -> ["a.id", "b.id"]
# Array paths
paths_to({users: [{id: 1}]}, "id") -> ["users.0.id"]
# Key not found
paths_to({a: 1}, "x") -> []
CLI Usage:
echo '{}' | jpx 'paths_to({a: {id: 1}, b: {id: 2}}, "id")'
pick
Select specific keys from object
Signature: object, array -> object
Examples:
# Pick one key
pick({a: 1, b: 2}, ['a']) -> {a: 1}
# Pick multiple keys
pick({a: 1, b: 2, c: 3}, ['a', 'c']) -> {a: 1, c: 3}
# Key not present
pick({a: 1}, ['x']) -> {}
# Empty pick list
pick({a: 1, b: 2}, []) -> {}
CLI Usage:
echo '{}' | jpx 'pick({a: 1, b: 2}, ['a'])'
pluck_deep
Find all values for a key anywhere in nested structure
Signature: any, string -> array
Examples:
# Find all ids
pluck_deep({users: [{id: 1}, {id: 2}], meta: {id: 99}}, "id") -> [1, 2, 99]
# Nested values
pluck_deep({a: {b: {c: 1}}, d: {c: 2}}, "c") -> [1, 2]
# Key not found
pluck_deep({a: 1}, "x") -> []
CLI Usage:
echo '{}' | jpx 'pluck_deep({users: [{id: 1}, {id: 2}], meta: {id: 99}}, "id")'
redact
Recursively replace values at specified keys with [REDACTED]
Signature: any, array -> any
Examples:
# Redact password
redact({name: "alice", password: "secret"}, ["password"]) -> {name: "alice", password: "[REDACTED]"}
# Nested redact
redact({user: {name: "bob", ssn: "123"}}, ["ssn"]) -> {user: {name: "bob", ssn: "[REDACTED]"}}
# Array of objects
redact([{token: "abc"}], ["token"]) -> [{token: "[REDACTED]"}]
CLI Usage:
echo '{}' | jpx 'redact({name: "alice", password: "secret"}, ["password"])'
redact_keys
Recursively redact keys matching a regex pattern
Signature: any, string -> any
Examples:
# Multiple patterns
redact_keys({password: "x", api_key: "y"}, "password|api_key") -> {password: "[REDACTED]", api_key: "[REDACTED]"}
# Wildcard pattern
redact_keys({secret_key: "a", secret_token: "b"}, "secret.*") -> {secret_key: "[REDACTED]", secret_token: "[REDACTED]"}
CLI Usage:
echo '{}' | jpx 'redact_keys({password: "x", api_key: "y"}, "password|api_key")'
remove_empty
Recursively remove nulls, empty strings, empty arrays, and empty objects
Signature: any -> any
Examples:
# Remove all empty values
remove_empty({a: \"\", b: [], c: {}, d: null, e: \"hello\"}) -> {e: \"hello\"}
# Nested cleanup
remove_empty({a: {b: \"\", c: 1}}) -> {a: {c: 1}}
# Array cleanup
remove_empty([\"\", \"hello\", [], null]) -> [\"hello\"]
CLI Usage:
echo '{}' | jpx 'remove_empty({a: \"\", b: [], c: {}, d: null, e: \"hello\"})'
remove_empty_strings
Recursively remove empty string values
Signature: any -> any
Examples:
# Remove empty strings
remove_empty_strings({name: \"alice\", bio: \"\"}) -> {name: \"alice\"}
# From arrays
remove_empty_strings([\"hello\", \"\", \"world\"]) -> [\"hello\", \"world\"]
# Nested
remove_empty_strings({a: {b: \"\", c: \"x\"}}) -> {a: {c: \"x\"}}
CLI Usage:
echo '{}' | jpx 'remove_empty_strings({name: \"alice\", bio: \"\"})'
remove_nulls
Recursively remove null values
Signature: any -> any
Examples:
# Remove nulls from object
remove_nulls({a: 1, b: null, c: 2}) -> {a: 1, c: 2}
# Nested nulls
remove_nulls({a: {b: null, c: 1}}) -> {a: {c: 1}}
# Remove from array
remove_nulls([1, null, 2]) -> [1, 2]
CLI Usage:
echo '{}' | jpx 'remove_nulls({a: 1, b: null, c: 2})'
rename_keys
Rename object keys
Signature: object, object -> object
Examples:
# Rename one key
rename_keys({a: 1}, {a: 'b'}) -> {b: 1}
# Rename multiple
rename_keys({a: 1, b: 2}, {a: 'x', b: 'y'}) -> {x: 1, y: 2}
# No matching key
rename_keys({a: 1}, {x: 'y'}) -> {a: 1}
# Real-world rename
rename_keys({old: 'value'}, {old: 'new'}) -> {new: 'value'}
CLI Usage:
echo '{}' | jpx 'rename_keys({a: 1}, {a: 'b'})'
set_path
Set value at JSON pointer path (immutable)
Signature: any, string, any -> any
Examples:
# Add new key
set_path({a: 1}, '/b', `2`) -> {a: 1, b: 2}
# Update existing
set_path({a: 1}, '/a', `2`) -> {a: 2}
# Set nested path
set_path({a: {}}, '/a/b', `1`) -> {a: {b: 1}}
# Update array element
set_path([1, 2], '/1', `5`) -> [1, 5]
CLI Usage:
echo '{}' | jpx 'set_path({a: 1}, `"/b"`, `2`)'
shouty_kebab_keys
Recursively convert all keys to SHOUTY-KEBAB-CASE
Signature: any -> any
Examples:
# Camel to shouty kebab
shouty_kebab_keys({userName: "alice"}) -> {"USER-NAME": "alice"}
# Snake to shouty kebab
shouty_kebab_keys({user_name: "bob"}) -> {"USER-NAME": "bob"}
# Nested
shouty_kebab_keys({userInfo: {firstName: "x"}}) -> {"USER-INFO": {"FIRST-NAME": "x"}}
CLI Usage:
echo '{}' | jpx 'shouty_kebab_keys({user_name: "alice"})'
shouty_snake_keys
Recursively convert all keys to SHOUTY_SNAKE_CASE
Signature: any -> any
Examples:
# Camel to shouty snake
shouty_snake_keys({userName: "alice"}) -> {USER_NAME: "alice"}
# Kebab to shouty snake
shouty_snake_keys({"user-name": "bob"}) -> {USER_NAME: "bob"}
# Nested
shouty_snake_keys({userInfo: {firstName: "x"}}) -> {USER_INFO: {FIRST_NAME: "x"}}
CLI Usage:
echo '{}' | jpx 'shouty_snake_keys({user_name: "alice"})'
snake_keys
Recursively convert all keys to snake_case
Signature: any -> any
Examples:
# Camel to snake
snake_keys({userName: "alice"}) -> {user_name: "alice"}
# Kebab to snake
snake_keys({"user-name": "bob"}) -> {user_name: "bob"}
# Nested
snake_keys({userInfo: {firstName: "x"}}) -> {user_info: {first_name: "x"}}
CLI Usage:
echo '{}' | jpx 'snake_keys({userName: "alice"})'
train_keys
Recursively convert all keys to Train-Case
Signature: any -> any
Examples:
# Camel to train
train_keys({userName: "alice"}) -> {"User-Name": "alice"}
# Snake to train
train_keys({user_name: "bob"}) -> {"User-Name": "bob"}
# Nested
train_keys({userInfo: {firstName: "x"}}) -> {"User-Info": {"First-Name": "x"}}
CLI Usage:
echo '{}' | jpx 'train_keys({user_name: "alice"})'
structural_diff
Compare two values and return their structural differences
Signature: any, any -> object
Examples:
# Changed value
structural_diff({a: 1}, {a: 2}) -> {changed: [{path: "a", from: 1, to: 2}], added: [], removed: []}
# Added key
structural_diff({a: 1}, {a: 1, b: 2}) -> {changed: [], added: [{path: "b", value: 2}], removed: []}
# Removed key
structural_diff({a: 1, b: 2}, {a: 1}) -> {changed: [], added: [], removed: [{path: "b", value: 2}]}
CLI Usage:
echo '{}' | jpx 'structural_diff({a: 1}, {a: 2})'
template
Expand a template string with values from an object using {{key}} syntax
Signature: object, string -> string
Examples:
# Simple substitution
template({name: "Alice"}, `"Hello, {{name}}!"`) -> "Hello, Alice!"
# Nested access
template({user: {name: "Bob"}}, `"Hi {{user.name}}"`) -> "Hi Bob"
# Default value
template({}, `"Hello {{name|World}}"`) -> "Hello World"
CLI Usage:
echo '{}' | jpx 'template({name: "Alice"}, `"Hello, {{name}}!"`)'
template_strict
Expand a template string, returning null if any variable is missing
Signature: object, string -> string | null
Examples:
# All vars present
template_strict({name: "Alice"}, `"Hello, {{name}}!"`) -> "Hello, Alice!"
# Missing variable
template_strict({}, `"Hello, {{name}}!"`) -> null
CLI Usage:
echo '{}' | jpx 'template_strict({name: "Alice"}, `"Hello, {{name}}!"`)'
truncate_to_size
Truncate an array to fit within approximately the specified byte size
Signature: array, number -> array
Examples:
# All fit
truncate_to_size([{a:1}, {b:2}, {c:3}], `100`) -> [{a:1}, {b:2}, {c:3}]
# Truncated
truncate_to_size([{a:1}, {b:2}, {c:3}], `15`) -> [{a:1}, {b:2}]
CLI Usage:
echo '{}' | jpx 'truncate_to_size([{a:1}, {b:2}, {c:3}], `100`)'
type_consistency
Check if array elements have consistent types
Signature: array -> object
Examples:
# Consistent numbers
type_consistency([1, 2, 3]).consistent -> true
# Mixed types
type_consistency([1, 'two', 3]).consistent -> false
# Object field mismatch
type_consistency([{a: 1}, {a: 'x'}]).inconsistencies -> [{field: 'a', ...}]
# Empty array is consistent
type_consistency([]).consistent -> true
CLI Usage:
echo '{}' | jpx 'type_consistency([1, 2, 3]).consistent'
unflatten
Alias for unflatten_keys - restore nested object from dot notation keys
Signature: object -> object
Examples:
# Simple nested
unflatten({\"a.b\": 1}) -> {a: {b: 1}}
# Deep nested
unflatten({\"a.b.c\": 1}) -> {a: {b: {c: 1}}}
# Flat keys
unflatten({\"a\": 1, \"b\": 2}) -> {a: 1, b: 2}
CLI Usage:
echo '{}' | jpx 'unflatten({\"a.b\": 1})'
unflatten_keys
Restore nested object from dot notation keys
Signature: object -> object
Examples:
# Simple nested
unflatten_keys({\"a.b\": 1}) -> {a: {b: 1}}
# Deep nested
unflatten_keys({\"a.b.c\": 1}) -> {a: {b: {c: 1}}}
# Flat keys
unflatten_keys({\"a\": 1, \"b\": 2}) -> {a: 1, b: 2}
# Mixed nesting
unflatten_keys({\"x.y\": 1, \"z\": 2}) -> {x: {y: 1}, z: 2}
CLI Usage:
echo '{}' | jpx 'unflatten_keys({\"a.b\": 1})'
with_entries
Transform object entries using an expression (jq parity)
Signature: object, string -> object
Examples:
# Transform keys and values
with_entries({a: 1, b: 2}, '[upper(@[0]), multiply(@[1], `2`)]') -> {A: 2, B: 4}
# Transform values only
with_entries({a: 1, b: 2}, '[@[0], add(@[1], `10`)]') -> {a: 11, b: 12}
# Filter entries
with_entries({a: 1, b: 2, c: 3}, 'if(@[1] > `1`, @, `null`)') -> {b: 2, c: 3}
CLI Usage:
echo '{}' | jpx 'with_entries({a: 1, b: 2}, `"[upper(@[0]), multiply(@[1], `2`)]"`)'
Math Functions
Mathematical and statistical functions: arithmetic, rounding, statistics, and number formatting.
Summary
| Function | Signature | Description |
|---|---|---|
abs_fn | number -> number | Absolute value |
add | number, number -> number | Add two numbers |
ceil_fn | number -> number | Round up to nearest integer |
clamp | number, number, number -> number | Clamp value to range |
cos | number -> number | Cosine function |
covariance | array, array -> number | Covariance between two arrays |
cumulative_sum | array -> array | Calculate running cumulative sum of a numeric array |
divide | number, number -> number | Divide first number by second |
ewma | array, number -> array | Exponential weighted moving average |
floor_fn | number -> number | Round down to nearest integer |
format_number | number, number?, string? -> string | Format number with separators and optional suffix |
log | number -> number | Natural logarithm |
median | array -> number | Calculate median of array |
mod_fn | number, number -> number | Modulo operation |
mode | array -> any | Find the most common value in an array |
moving_avg | array, number -> array | Simple moving average with window size |
multiply | number, number -> number | Multiply two numbers |
outliers_iqr | array, number? -> array | Find outliers using IQR method (values outside Q1-1.5IQR to Q3+1.5IQR) |
outliers_zscore | array, number? -> array | Find outliers using z-score method (values with |z-score| > threshold) |
percentile | array, number -> number | Calculate percentile of array |
pow | number, number -> number | Raise to power |
quantile | array, number -> number | Nth quantile (generalized percentile, q in [0,1]) |
quartiles | array -> object | Calculate quartiles (Q1, Q2, Q3) and IQR of array |
rate_of_change | array -> array | Calculate percentage change between consecutive values |
round | number, number -> number | Round to specified decimal places |
sin | number -> number | Sine function |
sqrt | number -> number | Square root |
standardize | array -> array | Standardize array to mean=0, std=1 (z-score normalization) |
stddev | array -> number | Calculate standard deviation of array |
subtract | number, number -> number | Subtract second number from first |
tan | number -> number | Tangent function |
to_fixed | number, number -> string | Format number with exact decimal places |
trend | array -> string | Detect trend direction in a numeric array (increasing, decreasing, or stable) |
trend_slope | array -> number | Calculate the linear regression slope of a numeric array |
variance | array -> number | Calculate variance of array |
Functions
abs_fn
Absolute value
Signature: number -> number
Examples:
# Negative to positive
abs_fn(`-5`) -> 5
# Already positive
abs_fn(`5`) -> 5
# Zero stays zero
abs_fn(`0`) -> 0
# Negative float
abs_fn(`-3.14`) -> 3.14
CLI Usage:
echo '{}' | jpx 'abs_fn(`-5`)'
add
Add two numbers
Signature: number, number -> number
Examples:
# Add integers
add(`2`, `3`) -> 5
# Add floats
add(`1.5`, `2.5`) -> 4.0
# With negative
add(`-5`, `3`) -> -2
# Add zero
add(`0`, `10`) -> 10
CLI Usage:
echo '{}' | jpx 'add(`2`, `3`)'
ceil_fn
Round up to nearest integer
Signature: number -> number
Examples:
# Round up fraction
ceil_fn(`3.2`) -> 4
# Round up high fraction
ceil_fn(`3.9`) -> 4
# Already integer
ceil_fn(`3.0`) -> 3
# Negative rounds toward zero
ceil_fn(`-3.2`) -> -3
CLI Usage:
echo '{}' | jpx 'ceil_fn(`3.2`)'
clamp
Clamp value to range
Signature: number, number, number -> number
Examples:
# Above max
clamp(`15`, `0`, `10`) -> 10
# Below min
clamp(`-5`, `0`, `10`) -> 0
# Within range
clamp(`5`, `0`, `10`) -> 5
# Custom range
clamp(`100`, `50`, `75`) -> 75
CLI Usage:
echo '{}' | jpx 'clamp(`15`, `0`, `10`)'
cos
Cosine function
Signature: number -> number
Examples:
# Cos of 0
cos(`0`) -> 1
# Cos of pi
cos(`3.14159`) -> -1
# Cos of pi/2
cos(`1.5708`) -> ~0
# Cos of 2*pi
cos(`6.28318`) -> 1
CLI Usage:
echo '{}' | jpx 'cos(`0`)'
covariance
Covariance between two arrays
Signature: array, array -> number
Examples:
# Perfect positive
covariance([1, 2, 3], [1, 2, 3]) -> 0.666...
# Perfect negative
covariance([1, 2, 3], [3, 2, 1]) -> -0.666...
# No correlation
covariance([1, 2, 3], [1, 1, 1]) -> 0
# From arrays
covariance(x_values, y_values) -> number
CLI Usage:
echo '{}' | jpx 'covariance([1, 2, 3], [1, 2, 3])'
cumulative_sum
Calculate running cumulative sum of a numeric array
Signature: array -> array
Examples:
# Running total
cumulative_sum([1, 2, 3, 4]) -> [1, 3, 6, 10]
# With negatives
cumulative_sum([10, -5, 3]) -> [10, 5, 8]
# Single element
cumulative_sum([100]) -> [100]
CLI Usage:
echo '{}' | jpx 'cumulative_sum([1, 2, 3, 4])'
divide
Divide first number by second
Signature: number, number -> number
Examples:
# Integer division
divide(`10`, `2`) -> 5
# Fractional result
divide(`7`, `2`) -> 3.5
# Repeating decimal
divide(`1`, `3`) -> 0.333...
# Negative dividend
divide(`-10`, `2`) -> -5
CLI Usage:
echo '{}' | jpx 'divide(`10`, `2`)'
ewma
Exponential weighted moving average
Signature: array, number -> array
Examples:
# Alpha 0.5
ewma([1, 2, 3, 4, 5], `0.5`) -> [1, 1.5, 2.25, ...]
# Smooth stock prices
ewma(prices, `0.3`) -> smoothed prices
# High alpha
ewma([10, 20, 30], `0.9`) -> fast response
# Low alpha
ewma([10, 20, 30], `0.1`) -> slow response
CLI Usage:
echo '{}' | jpx 'ewma([1, 2, 3, 4, 5], `0.5`)'
floor_fn
Round down to nearest integer
Signature: number -> number
Examples:
# Round down high fraction
floor_fn(`3.7`) -> 3
# Round down low fraction
floor_fn(`3.2`) -> 3
# Already integer
floor_fn(`3.0`) -> 3
# Negative rounds away from zero
floor_fn(`-3.2`) -> -4
CLI Usage:
echo '{}' | jpx 'floor_fn(`3.7`)'
format_number
Format number with separators and optional suffix
Signature: number, number?, string? -> string
Examples:
# With commas
format_number(`1234567`, `0`) -> \"1,234,567\"
# With decimals
format_number(`1234.56`, `2`) -> \"1,234.56\"
# Million
format_number(`1000000`, `0`) -> \"1,000,000\"
# Small number
format_number(`99.9`, `1`) -> \"99.9\"
CLI Usage:
echo '{}' | jpx 'format_number(`1234567`, `0`)'
log
Natural logarithm
Signature: number -> number
Examples:
# Log of e
log(`2.718`) -> ~1
# Log of 1
log(`1`) -> 0
# Log of 10
log(`10`) -> 2.302...
# Log of 100
log(`100`) -> 4.605...
CLI Usage:
echo '{}' | jpx 'log(`2.718`)'
median
Calculate median of array
Signature: array -> number
Examples:
# Odd count
median([1, 2, 3, 4, 5]) -> 3
# Even count
median([1, 2, 3, 4]) -> 2.5
# Middle value
median([10, 20, 30]) -> 20
# Single element
median([5]) -> 5
CLI Usage:
echo '{}' | jpx 'median([1, 2, 3, 4, 5])'
mod_fn
Modulo operation
Signature: number, number -> number
Examples:
# Remainder of 10/3
mod_fn(`10`, `3`) -> 1
# Evenly divisible
mod_fn(`15`, `5`) -> 0
# Odd check
mod_fn(`7`, `2`) -> 1
# Larger numbers
mod_fn(`100`, `7`) -> 2
CLI Usage:
echo '{}' | jpx 'mod_fn(`10`, `3`)'
mode
Find the most common value in an array
Signature: array -> any
Examples:
# Most frequent
mode([1, 2, 2, 3]) -> 2
# String mode
mode(['a', 'b', 'a', 'a']) -> 'a'
# Tie returns first
mode([1, 1, 2, 2, 3]) -> 1 or 2
# Single element
mode([5]) -> 5
CLI Usage:
echo '{}' | jpx 'mode([1, 2, 2, 3])'
moving_avg
Simple moving average with window size
Signature: array, number -> array
Examples:
# Window of 3
moving_avg([1, 2, 3, 4, 5], `3`) -> [null, null, 2, 3, 4]
# Window of 2
moving_avg([10, 20, 30, 40], `2`) -> [null, 15, 25, 35]
# 7-day moving avg
moving_avg(prices, `7`) -> weekly average
# Window of 1
moving_avg([1, 2, 3], `1`) -> [1, 2, 3]
CLI Usage:
echo '{}' | jpx 'moving_avg([1, 2, 3, 4, 5], `3`)'
multiply
Multiply two numbers
Signature: number, number -> number
Examples:
# Basic multiplication
multiply(`4`, `3`) -> 12
# With decimals
multiply(`2.5`, `4`) -> 10
# With negative
multiply(`-3`, `4`) -> -12
# Multiply by zero
multiply(`0`, `100`) -> 0
CLI Usage:
echo '{}' | jpx 'multiply(`4`, `3`)'
outliers_iqr
Find outliers using IQR method (values outside Q1-1.5IQR to Q3+1.5IQR)
Signature: array, number? -> array
Examples:
# Detect outlier
outliers_iqr([1, 2, 3, 4, 100]) -> [100]
# No outliers
outliers_iqr([1, 2, 3, 4, 5]) -> []
# Custom multiplier
outliers_iqr(values, `3.0`) -> extreme outliers only
# Find anomalies
outliers_iqr(measurements) -> anomalies
CLI Usage:
echo '{}' | jpx 'outliers_iqr([1, 2, 3, 4, 100])'
outliers_zscore
Find outliers using z-score method (values with |z-score| > threshold)
Signature: array, number? -> array
Examples:
# Detect outlier
outliers_zscore([1, 2, 3, 4, 100]) -> [100]
# No outliers
outliers_zscore([1, 2, 3, 4, 5]) -> []
# Custom threshold
outliers_zscore(values, `3.0`) -> extreme outliers only
# Lower threshold
outliers_zscore(measurements, `1.5`) -> more sensitive detection
CLI Usage:
echo '{}' | jpx 'outliers_zscore([1, 2, 3, 4, 100])'
percentile
Calculate percentile of array
Signature: array, number -> number
Examples:
# 50th percentile (median)
percentile([1, 2, 3, 4, 5], `50`) -> 3
# 25th percentile
percentile([1, 2, 3, 4, 5], `25`) -> 2
# 75th percentile
percentile([1, 2, 3, 4, 5], `75`) -> 4
# 90th percentile
percentile(scores, `90`) -> top 10% threshold
CLI Usage:
echo '{}' | jpx 'percentile([1, 2, 3, 4, 5], `50`)'
pow
Raise to power
Signature: number, number -> number
Examples:
# 2 cubed
pow(`2`, `3`) -> 8
# 10 squared
pow(`10`, `2`) -> 100
# 2^10
pow(`2`, `10`) -> 1024
# Square root via power
pow(`4`, `0.5`) -> 2
CLI Usage:
echo '{}' | jpx 'pow(`2`, `3`)'
quantile
Nth quantile (generalized percentile, q in [0,1])
Signature: array, number -> number
Examples:
# Median (0.5)
quantile([1, 2, 3, 4, 5], `0.5`) -> 3
# First quartile
quantile([1, 2, 3, 4, 5], `0.25`) -> 2
# Third quartile
quantile([1, 2, 3, 4, 5], `0.75`) -> 4
# 90th quantile
quantile(values, `0.9`) -> 90th quantile
CLI Usage:
echo '{}' | jpx 'quantile([1, 2, 3, 4, 5], `0.5`)'
quartiles
Calculate quartiles (Q1, Q2, Q3) and IQR of array
Signature: array -> object
Examples:
# Basic quartiles
quartiles([1, 2, 3, 4, 5]) -> {min: 1, q1: 2, q2: 3, q3: 4, max: 5, iqr: 2}
# Get IQR
quartiles([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).iqr -> 4.5
# Q2 equals median
quartiles(values).q2 == median(values) -> true
# Distribution analysis
quartiles(scores) -> full distribution stats
CLI Usage:
echo '{}' | jpx 'quartiles([1, 2, 3, 4, 5])'
rate_of_change
Calculate percentage change between consecutive values
Signature: array -> array
Examples:
# 10% growth
rate_of_change([100, 110, 121]) -> [10.0, 10.0]
# 50% decline
rate_of_change([100, 50, 25]) -> [-50.0, -50.0]
# Varying rate
rate_of_change([1, 2, 3]) -> [100.0, 50.0]
CLI Usage:
echo '{}' | jpx 'rate_of_change([100, 110, 121])'
round
Round to specified decimal places
Signature: number, number -> number
Examples:
# Two decimals
round(`3.14159`, `2`) -> 3.14
# Round to integer
round(`3.5`, `0`) -> 4
# Round up
round(`2.555`, `2`) -> 2.56
# One decimal
round(`123.456`, `1`) -> 123.5
CLI Usage:
echo '{}' | jpx 'round(`3.14159`, `2`)'
sin
Sine function
Signature: number -> number
Examples:
# Sin of 0
sin(`0`) -> 0
# Sin of pi/2
sin(`1.5708`) -> 1
# Sin of pi
sin(`3.14159`) -> ~0
# Sin of 2*pi
sin(`6.28318`) -> ~0
CLI Usage:
echo '{}' | jpx 'sin(`0`)'
sqrt
Square root
Signature: number -> number
Examples:
# Perfect square
sqrt(`16`) -> 4
# Irrational result
sqrt(`2`) -> 1.414...
# Larger number
sqrt(`100`) -> 10
# Zero
sqrt(`0`) -> 0
CLI Usage:
echo '{}' | jpx 'sqrt(`16`)'
standardize
Standardize array to mean=0, std=1 (z-score normalization)
Signature: array -> array
Examples:
# Basic z-scores
standardize([10, 20, 30]) -> [-1.22, 0, 1.22]
# Normalize values
standardize([1, 2, 3, 4, 5]) -> normalized
# Standardize scores
standardize(scores) -> z-scores
# Identical values
standardize([5, 5, 5]) -> [0, 0, 0]
CLI Usage:
echo '{}' | jpx 'standardize([10, 20, 30])'
stddev
Calculate standard deviation of array
Signature: array -> number
Examples:
# Basic stddev
stddev([1, 2, 3, 4, 5]) -> 1.414...
# No variation
stddev([10, 10, 10]) -> 0
# High variation
stddev([1, 100]) -> ~49.5
# Measure spread
stddev(values) -> spread measure
CLI Usage:
echo '{}' | jpx 'stddev([1, 2, 3, 4, 5])'
subtract
Subtract second number from first
Signature: number, number -> number
Examples:
# Basic subtraction
subtract(`5`, `3`) -> 2
# Negative result
subtract(`10`, `15`) -> -5
# With decimals
subtract(`3.5`, `1.5`) -> 2
# From zero
subtract(`0`, `5`) -> -5
CLI Usage:
echo '{}' | jpx 'subtract(`5`, `3`)'
tan
Tangent function
Signature: number -> number
Examples:
# Tan of 0
tan(`0`) -> 0
# Tan of pi/4
tan(`0.7854`) -> ~1
# Tan of pi
tan(`3.14159`) -> ~0
# Calculate tangent
tan(angle) -> ratio
CLI Usage:
echo '{}' | jpx 'tan(`0`)'
to_fixed
Format number with exact decimal places
Signature: number, number -> string
Examples:
# Two decimals
to_fixed(`3.14159`, `2`) -> \"3.14\"
# Pad with zeros
to_fixed(`5`, `2`) -> \"5.00\"
# Round to integer
to_fixed(`99.9`, `0`) -> \"100\"
# Extra precision
to_fixed(`1.5`, `3`) -> \"1.500\"
CLI Usage:
echo '{}' | jpx 'to_fixed(`3.14159`, `2`)'
trend
Detect trend direction in a numeric array (increasing, decreasing, or stable)
Signature: array -> string
Examples:
# Upward trend
trend([1, 2, 3, 4, 5]) -> "increasing"
# Downward trend
trend([5, 4, 3, 2, 1]) -> "decreasing"
# No change
trend([3, 3, 3, 3]) -> "stable"
CLI Usage:
echo '{}' | jpx 'trend([1, 2, 3, 4, 5])'
trend_slope
Calculate the linear regression slope of a numeric array
Signature: array -> number
Examples:
# Perfect linear increase
trend_slope([1, 2, 3, 4, 5]) -> 1.0
# Perfect linear decrease
trend_slope([5, 4, 3, 2, 1]) -> -1.0
# Noisy upward
trend_slope([1, 3, 2, 4]) -> ~0.7
CLI Usage:
echo '{}' | jpx 'trend_slope([1, 2, 3, 4, 5])'
variance
Calculate variance of array
Signature: array -> number
Examples:
# Basic variance
variance([1, 2, 3, 4, 5]) -> 2
# No variation
variance([10, 10, 10]) -> 0
# High variation
variance([1, 100]) -> 2450.25
# Squared spread
variance(values) -> spread^2
CLI Usage:
echo '{}' | jpx 'variance([1, 2, 3, 4, 5])'
Date/Time Functions
Functions for working with dates and times: parsing, formatting, arithmetic, and timezone handling.
Summary
| Function | Signature | Description |
|---|---|---|
business_days_between | number, number -> number | Count business days (weekdays) between two timestamps |
date_add | number, number, string -> number | Add time to timestamp |
date_diff | number, number, string -> number | Difference between timestamps |
duration_since | number|string -> object | Get detailed duration object from timestamp to now |
end_of_day | number|string -> string | Get ISO 8601 string for end of day (23:59:59) |
epoch_ms | -> number | Current Unix timestamp in milliseconds (alias for now_ms) |
format_date | number, string -> string | Format timestamp to string |
from_epoch | number -> string | Convert Unix timestamp (seconds) to ISO 8601 string |
from_epoch_ms | number -> string | Convert Unix timestamp (milliseconds) to ISO 8601 string |
is_after | number|string, number|string -> boolean | Check if first date is after second date (accepts timestamps or date strings) |
is_before | number|string, number|string -> boolean | Check if first date is before second date (accepts timestamps or date strings) |
is_between | number|string, number|string, number|string -> boolean | Check if date is between start and end (inclusive, accepts timestamps or date strings) |
is_same_day | number|string, number|string -> boolean | Check if two timestamps/dates are on the same day |
is_weekday | number -> boolean | Check if timestamp falls on weekday (Monday-Friday) |
is_weekend | number -> boolean | Check if timestamp falls on weekend (Saturday or Sunday) |
parse_date | string, string? -> number | Parse date string to timestamp |
quarter | number -> number | Get quarter of year (1-4) from timestamp |
relative_time | number -> string | Human-readable relative time from timestamp |
start_of_day | number|string -> string | Get ISO 8601 string for start of day (00:00:00) |
start_of_month | number|string -> string | Get ISO 8601 string for start of month |
start_of_week | number|string -> string | Get ISO 8601 string for start of week (Monday 00:00:00) |
start_of_year | number|string -> string | Get ISO 8601 string for start of year |
time_ago | number|string -> string | Human-readable time since date (accepts timestamps or date strings) |
timezone_convert | string, string, string -> string | Convert timestamp between timezones (IANA timezone names) |
to_epoch | number|string -> number | Convert date string or timestamp to Unix timestamp (seconds) |
to_epoch_ms | number|string -> number | Convert date string or timestamp to Unix timestamp (milliseconds) |
Functions
business_days_between
Count business days (weekdays) between two timestamps
Signature: number, number -> number
Examples:
# Count weekdays
business_days_between(`1704067200`, `1705276800`) -> 10
# Between two timestamps
business_days_between(start_ts, end_ts) -> count
# One week = 5 days
business_days_between(`0`, `604800`) -> 5
# Same day
business_days_between(`0`, `0`) -> 0
CLI Usage:
echo '{}' | jpx 'business_days_between(`1704067200`, `1705276800`)'
date_add
Add time to timestamp
Signature: number, number, string -> number
Examples:
# Add 1 day
date_add(`0`, `1`, 'days') -> 86400
# Add 2 hours
date_add(`0`, `2`, 'hours') -> 7200
# Add 1 week
date_add(timestamp, `7`, 'days') -> next week
# Add 30 minutes
date_add(`0`, `30`, 'minutes') -> 1800
CLI Usage:
echo '{}' | jpx 'date_add(`0`, `1`, `"days"`)'
date_diff
Difference between timestamps
Signature: number, number, string -> number
Examples:
# Diff in days
date_diff(`86400`, `0`, 'days') -> 1
# Diff in hours
date_diff(`7200`, `0`, 'hours') -> 2
# Diff in minutes
date_diff(end_ts, start_ts, 'minutes') -> minutes
# Diff in weeks
date_diff(`604800`, `0`, 'weeks') -> 1
CLI Usage:
echo '{}' | jpx 'date_diff(`86400`, `0`, `"days"`)'
duration_since
Get detailed duration object from timestamp to now
Signature: number|string -> object
Examples:
# From timestamp
duration_since(`1702396800`) -> {days: 1, hours: 0, ...}
# From date string
duration_since('2023-01-01') -> {days: N, ...}
# Time since creation
duration_since(created_at) -> elapsed time
# One hour ago
duration_since(now() - 3600) -> {hours: 1, ...}
CLI Usage:
echo '{}' | jpx 'duration_since(`1702396800`)'
end_of_day
Get ISO 8601 string for end of day (23:59:59)
Signature: number|string -> string
Examples:
# From ISO string
end_of_day('2023-12-13T15:30:00Z') -> \"2023-12-13T23:59:59Z\"
# From timestamp
end_of_day(`1702483200`) -> end of that day
# From date only
end_of_day('2024-01-01') -> last second of day
# End of today
end_of_day(now()) -> today end
CLI Usage:
echo '{}' | jpx 'end_of_day(`"2023-12-13T15:30:00Z"`)'
epoch_ms
Current Unix timestamp in milliseconds (alias for now_ms)
Signature: -> number
Examples:
# Current time in ms
epoch_ms() -> 1702483200000
# Convert to seconds
epoch_ms() / `1000` -> seconds
# Calculate duration
epoch_ms() - start_ms -> elapsed
CLI Usage:
echo '{}' | jpx 'epoch_ms()'
format_date
Format timestamp to string
Signature: number, string -> string
Examples:
# ISO date format
format_date(`1705276800`, '%Y-%m-%d') -> \"2024-01-15\"
# Long date format
format_date(ts, '%B %d, %Y') -> \"January 15, 2024\"
# Time only
format_date(ts, '%H:%M:%S') -> \"10:30:00\"
# Full ISO 8601
format_date(ts, '%Y-%m-%dT%H:%M:%SZ') -> ISO string
CLI Usage:
echo '{}' | jpx 'format_date(`1705276800`, `"%Y-%m-%d"`)'
from_epoch
Convert Unix timestamp (seconds) to ISO 8601 string
Signature: number -> string
Examples:
# Convert seconds
from_epoch(`1702483200`) -> \"2023-12-13T16:00:00Z\"
# Unix epoch
from_epoch(`0`) -> \"1970-01-01T00:00:00Z\"
# Convert field
from_epoch(created_at) -> ISO string
# Current time
from_epoch(now()) -> current ISO
CLI Usage:
echo '{}' | jpx 'from_epoch(`1702483200`)'
from_epoch_ms
Convert Unix timestamp (milliseconds) to ISO 8601 string
Signature: number -> string
Examples:
# From milliseconds
from_epoch_ms(`1702483200000`) -> \"2023-12-13T16:00:00Z\"
# Unix epoch
from_epoch_ms(`0`) -> \"1970-01-01T00:00:00Z\"
# JS timestamp
from_epoch_ms(js_timestamp) -> ISO
# Current time
from_epoch_ms(epoch_ms()) -> current
CLI Usage:
echo '{}' | jpx 'from_epoch_ms(`1702483200000`)'
is_after
Check if first date is after second date (accepts timestamps or date strings)
Signature: number|string, number|string -> boolean
Examples:
# String comparison
is_after('2024-07-15', '2024-01-01') -> true
# Timestamp comparison
is_after(`1705276800`, `1704067200`) -> true
# Field comparison
is_after(end_date, start_date) -> true/false
# Same date
is_after('2024-01-01', '2024-01-01') -> false
CLI Usage:
echo '{}' | jpx 'is_after(`"2024-07-15"`, `"2024-01-01"`)'
is_before
Check if first date is before second date (accepts timestamps or date strings)
Signature: number|string, number|string -> boolean
Examples:
# String comparison
is_before('2024-01-01', '2024-07-15') -> true
# Timestamp comparison
is_before(`1704067200`, `1705276800`) -> true
# Field comparison
is_before(start_date, end_date) -> true/false
# Same date
is_before('2024-01-01', '2024-01-01') -> false
CLI Usage:
echo '{}' | jpx 'is_before(`"2024-01-01"`, `"2024-07-15"`)'
is_between
Check if date is between start and end (inclusive, accepts timestamps or date strings)
Signature: number|string, number|string, number|string -> boolean
Examples:
# Within range
is_between('2024-06-15', '2024-01-01', '2024-12-31') -> true
# Before range
is_between('2023-06-15', '2024-01-01', '2024-12-31') -> false
# Check event date
is_between(event_date, start, end) -> true/false
# Inclusive start
is_between('2024-01-01', '2024-01-01', '2024-12-31') -> true
CLI Usage:
echo '{}' | jpx 'is_between(`"2024-06-15"`, `"2024-01-01"`, `"2024-12-31"`)'
is_same_day
Check if two timestamps/dates are on the same day
Signature: number|string, number|string -> boolean
Examples:
# Same day, different time
is_same_day('2023-12-13T10:00:00Z', '2023-12-13T23:00:00Z') -> true
# Different days
is_same_day('2023-12-13', '2023-12-14') -> false
# Compare fields
is_same_day(created_at, updated_at) -> true/false
# Today check
is_same_day(now(), event_time) -> true/false
CLI Usage:
echo '{}' | jpx 'is_same_day(`"2023-12-13T10:00:00Z"`, `"2023-12-13T23:00:00Z"`)'
is_weekday
Check if timestamp falls on weekday (Monday-Friday)
Signature: number -> boolean
Examples:
# Monday is weekday
is_weekday(`1705276800`) -> true
# Check current day
is_weekday(now()) -> true/false
# Check event day
is_weekday(event_timestamp) -> true/false
# Saturday is not weekday
is_weekday(`1705104000`) -> false
CLI Usage:
echo '{}' | jpx 'is_weekday(`1705276800`)'
is_weekend
Check if timestamp falls on weekend (Saturday or Sunday)
Signature: number -> boolean
Examples:
# Saturday is weekend
is_weekend(`1705104000`) -> true
# Check current day
is_weekend(now()) -> true/false
# Monday is not weekend
is_weekend(`1705276800`) -> false
# Check event day
is_weekend(event_timestamp) -> true/false
CLI Usage:
echo '{}' | jpx 'is_weekend(`1705104000`)'
parse_date
Parse date string to timestamp
Signature: string, string? -> number
Examples:
# ISO date format
parse_date('2024-01-15', '%Y-%m-%d') -> 1705276800
# US date format
parse_date('01/15/2024', '%m/%d/%Y') -> timestamp
# Named month
parse_date('15-Jan-2024', '%d-%b-%Y') -> timestamp
# With time
parse_date('2024-01-15T10:30:00', '%Y-%m-%dT%H:%M:%S') -> timestamp
CLI Usage:
echo '{}' | jpx 'parse_date(`"2024-01-15"`, `"%Y-%m-%d"`)'
quarter
Get quarter of year (1-4) from timestamp
Signature: number -> number
Examples:
# April is Q2
quarter(`1713139200`) -> 2
# January is Q1
quarter(`1704067200`) -> 1
# July is Q3
quarter(`1719792000`) -> 3
# November is Q4
quarter(`1730419200`) -> 4
CLI Usage:
echo '{}' | jpx 'quarter(`1713139200`)'
relative_time
Human-readable relative time from timestamp
Signature: number -> string
Examples:
# One hour ago
relative_time(now() - 3600) -> \"1 hour ago\"
# One minute ago
relative_time(now() - 60) -> \"1 minute ago\"
# One day ago
relative_time(now() - 86400) -> \"1 day ago\"
# From field
relative_time(created_at) -> human readable
CLI Usage:
echo '{}' | jpx 'relative_time(now() - 3600)'
start_of_day
Get ISO 8601 string for start of day (00:00:00)
Signature: number|string -> string
Examples:
# From ISO string
start_of_day('2023-12-13T15:30:00Z') -> \"2023-12-13T00:00:00Z\"
# From timestamp
start_of_day(`1702483200`) -> start of that day
# Today start
start_of_day(now()) -> today midnight
# From date only
start_of_day('2024-01-15') -> midnight
CLI Usage:
echo '{}' | jpx 'start_of_day(`"2023-12-13T15:30:00Z"`)'
start_of_month
Get ISO 8601 string for start of month
Signature: number|string -> string
Examples:
# From ISO string
start_of_month('2023-12-13T15:30:00Z') -> \"2023-12-01T00:00:00Z\"
# Current month start
start_of_month(now()) -> first of month
# From timestamp
start_of_month(`1702483200`) -> month start
# March start
start_of_month('2024-03-15') -> \"2024-03-01T00:00:00Z\"
CLI Usage:
echo '{}' | jpx 'start_of_month(`"2023-12-13T15:30:00Z"`)'
start_of_week
Get ISO 8601 string for start of week (Monday 00:00:00)
Signature: number|string -> string
Examples:
# Wednesday to Monday
start_of_week('2023-12-13T15:30:00Z') -> \"2023-12-11T00:00:00Z\"
# Current week start
start_of_week(now()) -> this Monday
# From timestamp
start_of_week(`1702483200`) -> week start
# Monday stays Monday
start_of_week('2024-01-15') -> \"2024-01-15T00:00:00Z\"
CLI Usage:
echo '{}' | jpx 'start_of_week(`"2023-12-13T15:30:00Z"`)'
start_of_year
Get ISO 8601 string for start of year
Signature: number|string -> string
Examples:
# From ISO string
start_of_year('2023-12-13T15:30:00Z') -> \"2023-01-01T00:00:00Z\"
# Current year start
start_of_year(now()) -> Jan 1st
# From timestamp
start_of_year(`1702483200`) -> year start
# Mid-year to Jan 1
start_of_year('2024-06-15') -> \"2024-01-01T00:00:00Z\"
CLI Usage:
echo '{}' | jpx 'start_of_year(`"2023-12-13T15:30:00Z"`)'
time_ago
Human-readable time since date (accepts timestamps or date strings)
Signature: number|string -> string
Examples:
# From date string
time_ago('2020-01-01') -> \"4 years ago\"
# One hour ago
time_ago(now() - 3600) -> \"1 hour ago\"
# From field
time_ago(created_at) -> human readable
# Months ago
time_ago('2023-12-01') -> \"X months ago\"
CLI Usage:
echo '{}' | jpx 'time_ago(`"2020-01-01"`)'
timezone_convert
Convert timestamp between timezones (IANA timezone names)
Signature: string, string, string -> string
Examples:
# NY to London
timezone_convert('2024-01-15T10:00:00', 'America/New_York', 'Europe/London') -> \"2024-01-15T15:00:00\"
# UTC to Pacific
timezone_convert(time, 'UTC', 'America/Los_Angeles') -> PST time
# Tokyo to UTC
timezone_convert(time, 'Asia/Tokyo', 'UTC') -> UTC time
# Paris to Chicago
timezone_convert(time, 'Europe/Paris', 'America/Chicago') -> CST time
CLI Usage:
echo '{}' | jpx 'timezone_convert(`"2024-01-15T10:00:00"`, `"America/New_York"`, `"Europe/London"`)'
to_epoch
Convert date string or timestamp to Unix timestamp (seconds)
Signature: number|string -> number
Examples:
# From ISO string
to_epoch('2023-12-13T16:00:00Z') -> 1702483200
# From date only
to_epoch('2024-01-01') -> timestamp
# Convert field
to_epoch(date_field) -> seconds
# Unix epoch
to_epoch('1970-01-01T00:00:00Z') -> 0
CLI Usage:
echo '{}' | jpx 'to_epoch(`"2023-12-13T16:00:00Z"`)'
to_epoch_ms
Convert date string or timestamp to Unix timestamp (milliseconds)
Signature: number|string -> number
Examples:
# From ISO string
to_epoch_ms('2023-12-13T16:00:00Z') -> 1702483200000
# From date only
to_epoch_ms('2024-01-01') -> timestamp_ms
# Convert field
to_epoch_ms(date_field) -> milliseconds
# Unix epoch
to_epoch_ms('1970-01-01T00:00:00Z') -> 0
CLI Usage:
echo '{}' | jpx 'to_epoch_ms(`"2023-12-13T16:00:00Z"`)'
Duration Functions
Functions for working with time durations.
Summary
| Function | Signature | Description |
|---|---|---|
duration_hours | number -> number | Convert seconds to hours |
duration_minutes | number -> number | Convert seconds to minutes |
duration_seconds | number -> number | Get seconds component |
format_duration | number -> string | Format seconds as duration string |
parse_duration | string -> number | Parse duration string to seconds |
Functions
duration_hours
Convert seconds to hours
Signature: number -> number
Examples:
# 2 hours
duration_hours(`7200`) -> 2
# 1 hour
duration_hours(`3600`) -> 1
# 1.5 hours
duration_hours(`5400`) -> 1.5
CLI Usage:
echo '{}' | jpx 'duration_hours(`7200`)'
duration_minutes
Convert seconds to minutes
Signature: number -> number
Examples:
# 2 minutes
duration_minutes(`120`) -> 2
# 1 minute
duration_minutes(`60`) -> 1
# 1.5 minutes
duration_minutes(`90`) -> 1.5
CLI Usage:
echo '{}' | jpx 'duration_minutes(`120`)'
duration_seconds
Get seconds component
Signature: number -> number
Examples:
# 65 seconds mod 60
duration_seconds(`65`) -> 5
# Exact minutes
duration_seconds(`120`) -> 0
# 1 hour 1 min 1 sec
duration_seconds(`3661`) -> 1
CLI Usage:
echo '{}' | jpx 'duration_seconds(`65`)'
format_duration
Format seconds as duration string
Signature: number -> string
Examples:
# 1.5 hours
format_duration(`5400`) -> \"1h30m\"
# 1 hour 1 min 1 sec
format_duration(`3661`) -> \"1h1m1s\"
# 1 minute
format_duration(`60`) -> \"1m\"
CLI Usage:
echo '{}' | jpx 'format_duration(`5400`)'
parse_duration
Parse duration string to seconds
Signature: string -> number
Examples:
# 1.5 hours
parse_duration('1h30m') -> 5400
# 2 hours
parse_duration('2h') -> 7200
# 30 seconds
parse_duration('30s') -> 30
CLI Usage:
echo '{}' | jpx 'parse_duration(`"1h30m"`)'
Hash Functions
Cryptographic hash functions: MD5, SHA family, and other hash algorithms.
Summary
| Function | Signature | Description |
|---|---|---|
crc32 | string -> number | Calculate CRC32 checksum |
hmac_md5 | string, string -> string | Calculate HMAC-MD5 signature |
hmac_sha1 | string, string -> string | Calculate HMAC-SHA1 signature |
hmac_sha256 | string, string -> string | Calculate HMAC-SHA256 signature |
hmac_sha512 | string, string -> string | Calculate HMAC-SHA512 signature |
md5 | string -> string | Calculate MD5 hash |
sha1 | string -> string | Calculate SHA-1 hash |
sha256 | string -> string | Calculate SHA-256 hash |
sha512 | string -> string | Calculate SHA-512 hash |
Functions
crc32
Calculate CRC32 checksum
Signature: string -> number
Examples:
# Simple string
crc32('hello') -> 907060870
# Empty string
crc32('') -> 0
# Data with space
crc32('test data') -> 2706273274
CLI Usage:
echo '{}' | jpx 'crc32(`"hello"`)'
hmac_md5
Calculate HMAC-MD5 signature
Signature: string, string -> string
Examples:
# With secret key
hmac_md5('hello', 'secret') -> \"e17e4e4a205c55782dce5b6ff41e6e19\"
# Different message
hmac_md5('message', 'key') -> \"a24c903c3a7e7b741ea77bd467b98bca\"
CLI Usage:
echo '{}' | jpx 'hmac_md5(`"hello"`, `"secret"`)'
hmac_sha1
Calculate HMAC-SHA1 signature
Signature: string, string -> string
Examples:
# With secret key
hmac_sha1('hello', 'secret') -> \"5112055c36b16a6693045d75a054332e4555b52f\"
# Different data
hmac_sha1('data', 'key') -> \"104152c5bfdca07bc633eebd46199f0255c9f49d\"
CLI Usage:
echo '{}' | jpx 'hmac_sha1(`"hello"`, `"secret"`)'
hmac_sha256
Calculate HMAC-SHA256 signature
Signature: string, string -> string
Examples:
# With secret key
hmac_sha256('hello', 'secret') -> \"88aab3ede8d3adf94d26ab90d3bafd4a2083070c3bcce9c014ee04a443847c0b\"
# Different data
hmac_sha256('data', 'key') -> \"5031fe3d989c6d1537a013fa6e739da23463fdaec3b70137d828e36ace221bd0\"
CLI Usage:
echo '{}' | jpx 'hmac_sha256(`"hello"`, `"secret"`)'
hmac_sha512
Calculate HMAC-SHA512 signature
Signature: string, string -> string
Examples:
# With secret key
hmac_sha512('hello', 'secret') -> \"d05888a20ae...\"
# Different data
hmac_sha512('data', 'key') -> \"3c5953a18...\"
CLI Usage:
echo '{}' | jpx 'hmac_sha512(`"hello"`, `"secret"`)'
md5
Calculate MD5 hash
Signature: string -> string
Examples:
# Simple string
md5('hello') -> \"5d41402abc4b2a76b9719d911017c592\"
# Empty string
md5('') -> \"d41d8cd98f00b204e9800998ecf8427e\"
# Another string
md5('test') -> \"098f6bcd4621d373cade4e832627b4f6\"
CLI Usage:
echo '{}' | jpx 'md5(`"hello"`)'
sha1
Calculate SHA-1 hash
Signature: string -> string
Examples:
# Simple string
sha1('hello') -> \"aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d\"
# Empty string
sha1('') -> \"da39a3ee5e6b4b0d3255bfef95601890afd80709\"
# Another string
sha1('test') -> \"a94a8fe5ccb19ba61c4c0873d391e987982fbbd3\"
CLI Usage:
echo '{}' | jpx 'sha1(`"hello"`)'
sha256
Calculate SHA-256 hash
Signature: string -> string
Examples:
# Simple string
sha256('hello') -> \"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824\"
# Empty string
sha256('') -> \"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\"
# Another string
sha256('test') -> \"9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08\"
CLI Usage:
echo '{}' | jpx 'sha256(`"hello"`)'
sha512
Calculate SHA-512 hash
Signature: string -> string
Examples:
# Simple string
sha512('hello') -> \"9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e5c3adef46f73bcdec043\"
# Another string
sha512('test') -> \"ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff\"
CLI Usage:
echo '{}' | jpx 'sha512(`"hello"`)'
Encoding Functions
Encoding and decoding functions: Base64, hex, URL encoding, and more.
Summary
| Function | Signature | Description |
|---|---|---|
base64_decode | string -> string | Decode base64 string |
base64_encode | string -> string | Encode string to base64 |
hex_decode | string -> string | Decode hex string |
hex_encode | string -> string | Encode string to hex |
html_escape | string -> string | Escape HTML special characters |
html_unescape | string -> string | Unescape HTML entities |
jwt_decode | string -> object | Decode JWT payload (claims) without verification |
jwt_header | string -> object | Decode JWT header without verification |
Functions
base64_decode
Decode base64 string
Signature: string -> string
Examples:
# Decode hello
base64_decode('aGVsbG8=') -> \"hello\"
# Decode test
base64_decode('dGVzdA==') -> \"test\"
# Empty string
base64_decode('') -> \"\"
CLI Usage:
echo '{}' | jpx 'base64_decode(`"aGVsbG8="`)'
base64_encode
Encode string to base64
Signature: string -> string
Examples:
# Encode hello
base64_encode('hello') -> \"aGVsbG8=\"
# Encode test
base64_encode('test') -> \"dGVzdA==\"
# Empty string
base64_encode('') -> \"\"
CLI Usage:
echo '{}' | jpx 'base64_encode(`"hello"`)'
hex_decode
Decode hex string
Signature: string -> string
Examples:
# Decode hello
hex_decode('68656c6c6f') -> \"hello\"
# Decode test
hex_decode('74657374') -> \"test\"
# Empty string
hex_decode('') -> \"\"
CLI Usage:
echo '{}' | jpx 'hex_decode(`"68656c6c6f"`)'
hex_encode
Encode string to hex
Signature: string -> string
Examples:
# Encode hello
hex_encode('hello') -> \"68656c6c6f\"
# Encode test
hex_encode('test') -> \"74657374\"
# Empty string
hex_encode('') -> \"\"
CLI Usage:
echo '{}' | jpx 'hex_encode(`"hello"`)'
html_escape
Escape HTML special characters
Signature: string -> string
Examples:
# Escape tags
html_escape('<div>') -> \"<div>\"
# Escape ampersand
html_escape('a & b') -> \"a & b\"
# Escape quotes
html_escape('\"quoted\"') -> \""quoted"\"
# Sanitize input
html_escape(user_input) -> safe for HTML
CLI Usage:
echo '{}' | jpx 'html_escape(`"<div>"`)'
html_unescape
Unescape HTML entities
Signature: string -> string
Examples:
# Unescape tags
html_unescape('<div>') -> \"<div>\"
# Unescape ampersand
html_unescape('a & b') -> \"a & b\"
# Unescape quotes
html_unescape('"quoted"') -> \"\\\"quoted\\\"\"
# Decode entities
html_unescape(escaped) -> original
CLI Usage:
echo '{}' | jpx 'html_unescape(`"<div>"`)'
jwt_decode
Decode JWT payload (claims) without verification
Signature: string -> object
Examples:
# Extract subject claim
jwt_decode('eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyXzEyMyJ9.sig').sub -> \"user_123\"
# Extract name claim
jwt_decode('eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiSm9obiJ9.sig').name -> 'John'
CLI Usage:
echo '{}' | jpx 'jwt_decode(`"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyXzEyMyJ9.sig"`).sub'
jwt_header
Decode JWT header without verification
Signature: string -> object
Examples:
# Extract algorithm
jwt_header('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.payload.sig').alg -> \"HS256\"
# Extract type
jwt_header('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.payload.sig').typ -> \"JWT\"
CLI Usage:
echo '{}' | jpx 'jwt_header(`"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.payload.sig"`).alg'
Validation Functions
Functions for validating data: email, URL, UUID, and format validation.
Summary
| Function | Signature | Description |
|---|---|---|
is_base64 | string -> boolean | Check if valid Base64 encoding |
is_credit_card | string -> boolean | Validate credit card number (Luhn check + length) |
is_email | string -> boolean | Validate email address format |
is_hex | string -> boolean | Check if valid hexadecimal string |
is_ipv4 | string -> boolean | Validate IPv4 address format |
is_ipv6 | string -> boolean | Validate IPv6 address format |
is_iso_date | string -> boolean | Validate ISO 8601 date format |
is_json | string -> boolean | Check if string is valid JSON |
is_jwt | string -> boolean | Check if valid JWT structure (3 base64url parts) |
is_phone | string -> boolean | Validate phone number format |
is_url | string -> boolean | Validate URL format |
is_uuid | string -> boolean | Validate UUID format |
luhn_check | string -> boolean | Generic Luhn algorithm check |
Functions
is_base64
Check if valid Base64 encoding
Signature: string -> boolean
Examples:
# Valid base64
is_base64('SGVsbG8=') -> true
# Invalid chars
is_base64('not valid!') -> false
# Empty is valid
is_base64('') -> true
CLI Usage:
echo '{}' | jpx 'is_base64(`"SGVsbG8="`)'
is_credit_card
Validate credit card number (Luhn check + length)
Signature: string -> boolean
Examples:
# Valid Visa test number
is_credit_card('4111111111111111') -> true
# Invalid number
is_credit_card('1234567890123456') -> false
# Valid Mastercard test
is_credit_card('5500000000000004') -> true
CLI Usage:
echo '{}' | jpx 'is_credit_card(`"4111111111111111"`)'
is_email
Validate email address format
Signature: string -> boolean
Examples:
# Valid email
is_email('user@example.com') -> true
# Missing @
is_email('invalid-email') -> false
# Subdomain
is_email('user@sub.domain.com') -> true
CLI Usage:
echo '{}' | jpx 'is_email(`"user@example.com"`)'
is_hex
Check if valid hexadecimal string
Signature: string -> boolean
Examples:
# Valid hex
is_hex('deadbeef') -> true
# Uppercase hex
is_hex('ABCDEF') -> true
# Invalid chars
is_hex('ghijkl') -> false
CLI Usage:
echo '{}' | jpx 'is_hex(`"deadbeef"`)'
is_ipv4
Validate IPv4 address format
Signature: string -> boolean
Examples:
# Valid IPv4
is_ipv4('192.168.1.1') -> true
# Out of range
is_ipv4('256.1.1.1') -> false
# Private IP
is_ipv4('10.0.0.1') -> true
CLI Usage:
echo '{}' | jpx 'is_ipv4(`"192.168.1.1"`)'
is_ipv6
Validate IPv6 address format
Signature: string -> boolean
Examples:
# Loopback
is_ipv6('::1') -> true
# Full IPv6
is_ipv6('2001:db8::1') -> true
# IPv4 is not IPv6
is_ipv6('192.168.1.1') -> false
CLI Usage:
echo '{}' | jpx 'is_ipv6(`"::1"`)'
is_iso_date
Validate ISO 8601 date format
Signature: string -> boolean
Examples:
# Full ISO format
is_iso_date('2023-12-13T15:30:00Z') -> true
# Date only
is_iso_date('2023-12-13') -> true
# US format invalid
is_iso_date('12/13/2023') -> false
CLI Usage:
echo '{}' | jpx 'is_iso_date(`"2023-12-13T15:30:00Z"`)'
is_json
Check if string is valid JSON
Signature: string -> boolean
Examples:
# Valid JSON object
is_json('{\"a\": 1}') -> true
# Valid JSON array
is_json('[1, 2, 3]') -> true
# Invalid JSON
is_json('not json') -> false
CLI Usage:
echo '{}' | jpx 'is_json(`"{\"a\": 1}"`)'
is_jwt
Check if valid JWT structure (3 base64url parts)
Signature: string -> boolean
Examples:
# Valid JWT
is_jwt('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U') -> true
# Three parts with dots
is_jwt('eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiSm9obiJ9.signature') -> true
# Four parts
is_jwt('not.a.valid.jwt') -> false
# No dots
is_jwt('invalid') -> false
CLI Usage:
echo '{}' | jpx 'is_jwt(`"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U"`)'
is_phone
Validate phone number format
Signature: string -> boolean
Examples:
# US format with country code
is_phone('+1-555-123-4567') -> true
# US format without country code
is_phone('555-123-4567') -> true
# UK format
is_phone('+44 20 7946 0958') -> true
# Invalid phone
is_phone('invalid') -> false
CLI Usage:
echo '{}' | jpx 'is_phone(`"+1-555-123-4567"`)'
is_url
Validate URL format
Signature: string -> boolean
Examples:
# Simple HTTPS URL
is_url('https://example.com') -> true
# URL with port and path
is_url('http://localhost:8080/path') -> true
# FTP URL
is_url('ftp://files.example.com') -> true
# Invalid URL
is_url('not a url') -> false
CLI Usage:
echo '{}' | jpx 'is_url(`"https://example.com"`)'
is_uuid
Validate UUID format
Signature: string -> boolean
Examples:
# Valid UUID v4
is_uuid('550e8400-e29b-41d4-a716-446655440000') -> true
# Valid UUID v1
is_uuid('6ba7b810-9dad-11d1-80b4-00c04fd430c8') -> true
# Nil UUID
is_uuid('00000000-0000-0000-0000-000000000000') -> true
# Invalid format
is_uuid('not-a-uuid') -> false
CLI Usage:
echo '{}' | jpx 'is_uuid(`"550e8400-e29b-41d4-a716-446655440000"`)'
luhn_check
Generic Luhn algorithm check
Signature: string -> boolean
Examples:
# Valid Luhn number
luhn_check('79927398713') -> true
# Valid credit card number
luhn_check('4532015112830366') -> true
# Invalid Luhn
luhn_check('1234567890') -> false
# Single zero
luhn_check('0') -> true
CLI Usage:
echo '{}' | jpx 'luhn_check(`"79927398713"`)'
Expression Functions
Higher-order functions that work with JMESPath expressions as arguments.
Summary
| Function | Signature | Description |
|---|---|---|
all_expr | array, expression -> boolean | Return true if every element satisfies the expression (short-circuits on false) |
any_expr | array, expression -> boolean | Return true if any element satisfies the expression (short-circuits) |
apply | object|string, ...any -> any | Apply a partial function or invoke a function by name with arguments |
count_by | string, array -> object | Count occurrences grouped by expression result |
count_expr | array, expression -> number | Count how many elements satisfy the expression |
drop_while | string, array -> array | Drop elements from array while expression is truthy |
every | string, array -> boolean | Check if all elements match (alias for all_expr) |
filter_expr | array, expression -> array | Keep elements where JMESPath expression evaluates to truthy value |
find_expr | array, expression -> any | Return first element where expression is truthy, or null if none match |
find_index_expr | array, expression -> number | null | Return zero-based index of first matching element, or -1 if none match |
flat_map_expr | array, expression -> array | Apply expression to each element and flatten all results into one array |
group_by_expr | array, expression -> object | Group elements into object keyed by expression result |
map_expr | array, expression -> array | Apply a JMESPath expression to each element, returning transformed array |
map_keys | string, object -> object | Transform object keys using expression |
map_values | string, object -> object | Transform object values using expression |
max_by_expr | array, expression -> any | Return element with largest expression result, or null for empty array |
min_by_expr | array, expression -> any | Return element with smallest expression result, or null for empty array |
order_by | array, array[[string, string]] -> array | Sort array by multiple fields with ascending/descending control |
partial | string, ...any -> object | Create a partial function with some arguments pre-filled |
partition_expr | array, expression -> array | Split array into [matches, non-matches] based on expression |
recurse | any -> array | Collect all nested values recursively (jq parity) |
recurse_with | any, expression -> array | Recursive descent with expression filter (jq parity) |
reduce_expr | string, array, any -> any | Reduce array to single value using accumulator expression |
reject | string, array -> array | Keep elements where expression is falsy (inverse of filter_expr) |
scan_expr | string, array, any -> array | Like reduce but returns array of intermediate accumulator values |
some | string, array -> boolean | Check if any element matches (alias for any_expr) |
sort_by_expr | array, expression -> array | Sort array by expression result in ascending order |
take_while | string, array -> array | Take elements from array while expression is truthy |
unique_by_expr | array, expression -> array | Remove duplicates by expression result, keeping first occurrence |
until_expr | any, expression, expression -> array | Loop until condition becomes true, collecting intermediate values (jq parity) |
walk | string, any -> any | Recursively apply expression to all components of a value (bottom-up) |
while_expr | any, expression, expression -> array | Loop while condition is true, collecting intermediate values (jq parity) |
zip_with | string, array, array -> array | Zip two arrays with a custom combiner expression |
Functions
all_expr
Return true if every element satisfies the expression (short-circuits on false)
Signature: array, expression -> boolean
Aliases: every
Examples:
# All positive
all_expr([1, 2, 3], &@ > `0`) -> true
# Not all > 2
all_expr([1, 2, 3], &@ > `2`) -> false
# Empty array is true
all_expr([], &@ > `0`) -> true
# Check all adults
all_expr(users, &age >= `18`) -> true/false
CLI Usage:
echo '{}' | jpx 'all_expr([1, 2, 3], &@ > `0`)'
any_expr
Return true if any element satisfies the expression (short-circuits)
Signature: array, expression -> boolean
Aliases: some
Examples:
# At least one > 2
any_expr([1, 2, 3], &@ > `2`) -> true
# None > 5
any_expr([1, 2, 3], &@ > `5`) -> false
# Empty array is false
any_expr([], &@ > `0`) -> false
# Any active user
any_expr(users, &status == 'active') -> true/false
CLI Usage:
echo '{}' | jpx 'any_expr([1, 2, 3], &@ > `2`)'
apply
Apply a partial function or invoke a function by name with arguments
Signature: object|string, ...any -> any
Examples:
# Apply partial function
apply(partial('join', `\"-\"`), `[\"a\", \"b\"]`) -> 'a-b'
# Call function by name
apply('length', 'hello') -> 5
# Call with multiple args
apply('add', `1`, `2`) -> 3
# Partial with contains
apply(partial('contains', 'hello'), 'ell') -> true
CLI Usage:
echo '{}' | jpx 'apply(partial(`"join"`, `\"-\"`), `[\"a\", \"b\"]`)'
count_by
Count occurrences grouped by expression result
Signature: string, array -> object
Examples:
# Count by field
count_by('type', [{type: 'a'}, {type: 'b'}, {type: 'a'}]) -> {a: 2, b: 1}
# Count orders by status
count_by('status', orders) -> {pending: 5, shipped: 3}
# Count by condition
count_by('@ > `50`', [30, 60, 70, 40]) -> {true: 2, false: 2}
# Empty array
count_by('category', []) -> {}
CLI Usage:
echo '{}' | jpx 'count_by(`"type"`, [{type: 'a'}, {type: 'b'}, {type: 'a'}])'
count_expr
Count how many elements satisfy the expression
Signature: array, expression -> number
Examples:
# Count > 1
count_expr([1, 2, 3], &@ > `1`) -> 2
# None match
count_expr([1, 2, 3], &@ > `5`) -> 0
# Count active users
count_expr(users, &active == `true`) -> 5
# Empty array
count_expr([], &@ > `0`) -> 0
CLI Usage:
echo '{}' | jpx 'count_expr([1, 2, 3], &@ > `1`)'
drop_while
Drop elements from array while expression is truthy
Signature: string, array -> array
Examples:
# Drop while < 4
drop_while('@ < `4`', [1, 2, 3, 5, 1]) -> [5, 1]
# None dropped
drop_while('@ < `0`', [1, 2, 3]) -> [1, 2, 3]
# All dropped
drop_while('@ < `10`', [1, 2, 3]) -> []
# Drop short strings
drop_while('length(@) < `3`', ['a', 'ab', 'abc', 'x']) -> ['abc', 'x']
CLI Usage:
echo '{}' | jpx 'drop_while(`"@ < `4`"`, [1, 2, 3, 5, 1])'
every
Check if all elements match (alias for all_expr)
Signature: string, array -> boolean
Examples:
# All positive
every('@ > `0`', [1, 2, 3]) -> true
# Not all > 2
every('@ > `2`', [1, 2, 3]) -> false
# All non-empty
every('length(@) > `0`', ['a', 'b']) -> true
# Empty is vacuously true
every('@ > `0`', []) -> true
CLI Usage:
echo '{}' | jpx 'every(`"@ > `0`"`, [1, 2, 3])'
filter_expr
Keep elements where JMESPath expression evaluates to truthy value
Signature: array, expression -> array
Aliases: filter
Examples:
# Filter numbers
filter_expr([1, 2, 3], &@ > `1`) -> [2, 3]
# Filter objects by field
filter_expr(users, &age >= `18`) -> [adult users]
# Filter empty strings
filter_expr(['a', '', 'b'], &length(@) > `0`) -> ['a', 'b']
# Empty array returns empty
filter_expr([], &@ > `0`) -> []
CLI Usage:
echo '{}' | jpx 'filter_expr([1, 2, 3], &@ > `1`)'
find_expr
Return first element where expression is truthy, or null if none match
Signature: array, expression -> any
Examples:
# First > 1
find_expr([1, 2, 3], &@ > `1`) -> 2
# None found
find_expr([1, 2, 3], &@ > `5`) -> null
# Find user
find_expr(users, &name == 'alice') -> {name: 'alice', ...}
# Empty array
find_expr([], &@ > `0`) -> null
CLI Usage:
echo '{}' | jpx 'find_expr([1, 2, 3], &@ > `1`)'
find_index_expr
Return zero-based index of first matching element, or -1 if none match
Signature: array, expression -> number | null
Examples:
# Index of first > 1
find_index_expr([1, 2, 3], &@ > `1`) -> 1
# Not found
find_index_expr([1, 2, 3], &@ > `5`) -> -1
# Exact match
find_index_expr([5, 10, 15], &@ == `10`) -> 1
# Empty array
find_index_expr([], &@ > `0`) -> -1
CLI Usage:
echo '{}' | jpx 'find_index_expr([1, 2, 3], &@ > `1`)'
flat_map_expr
Apply expression to each element and flatten all results into one array
Signature: array, expression -> array
Examples:
# Flatten nested arrays
flat_map_expr([[1], [2]], &@) -> [1, 2]
# Duplicate and transform
flat_map_expr([1, 2], &[@, @ * `2`]) -> [1, 2, 2, 4]
# Flatten object arrays
flat_map_expr(users, &tags) -> all tags flattened
# Empty array
flat_map_expr([], &@) -> []
CLI Usage:
echo '{}' | jpx 'flat_map_expr([[1], [2]], &@)'
group_by_expr
Group elements into object keyed by expression result
Signature: array, expression -> object
Examples:
# Group by field
group_by_expr([{t: 'a'}, {t: 'b'}], &t) -> {a: [...], b: [...]}
# Group by condition
group_by_expr([1, 2, 3, 4], &@ > `2`) -> {true: [3, 4], false: [1, 2]}
# Group users
group_by_expr(users, &department) -> {eng: [...], sales: [...]}
# Empty array
group_by_expr([], &@) -> {}
CLI Usage:
echo '{}' | jpx 'group_by_expr([{t: 'a'}, {t: 'b'}], &t)'
map_expr
Apply a JMESPath expression to each element, returning transformed array
Signature: array, expression -> array
Examples:
# Double each number
map_expr([1, 2], &@ * `2`) -> [2, 4]
# Extract field from objects
map_expr(users, &name) -> ['alice', 'bob']
# Transform strings
map_expr(['hello', 'world'], &upper(@)) -> ['HELLO', 'WORLD']
# Empty array returns empty
map_expr([], &@ * `2`) -> []
CLI Usage:
echo '{}' | jpx 'map_expr([1, 2], &@ * `2`)'
map_keys
Transform object keys using expression
Signature: string, object -> object
Examples:
# Uppercase keys
map_keys('upper(@)', {a: 1}) -> {A: 1}
# Lowercase keys
map_keys('lower(@)', {A: 1, B: 2}) -> {a: 1, b: 2}
# Add suffix
map_keys('concat(@, `"_suffix"`)', {x: 1}) -> {x_suffix: 1}
# Empty object
map_keys('upper(@)', {}) -> {}
CLI Usage:
echo '{}' | jpx 'map_keys(`"upper(@)"`, {a: 1})'
map_values
Transform object values using expression
Signature: string, object -> object
Examples:
# Double values
map_values('@ * `2`', {a: 1, b: 2}) -> {a: 2, b: 4}
# Uppercase strings
map_values('upper(@)', {a: 'x', b: 'y'}) -> {a: 'X', b: 'Y'}
# Add to values
map_values('@ + `10`', {x: 5}) -> {x: 15}
# Empty object
map_values('@ * `2`', {}) -> {}
CLI Usage:
echo '{}' | jpx 'map_values(`"@ * `2`"`, {a: 1, b: 2})'
max_by_expr
Return element with largest expression result, or null for empty array
Signature: array, expression -> any
Examples:
# Max by field
max_by_expr([{a: 2}, {a: 1}], &a) -> {a: 2}
# Longest string
max_by_expr(['a', 'abc', 'ab'], &length(@)) -> 'abc'
# Max price product
max_by_expr(products, &price) -> most expensive
# Empty array
max_by_expr([], &a) -> null
CLI Usage:
echo '{}' | jpx 'max_by_expr([{a: 2}, {a: 1}], &a)'
min_by_expr
Return element with smallest expression result, or null for empty array
Signature: array, expression -> any
Examples:
# Min by field
min_by_expr([{a: 2}, {a: 1}], &a) -> {a: 1}
# Shortest string
min_by_expr(['a', 'abc', 'ab'], &length(@)) -> 'a'
# Min price product
min_by_expr(products, &price) -> cheapest
# Empty array
min_by_expr([], &a) -> null
CLI Usage:
echo '{}' | jpx 'min_by_expr([{a: 2}, {a: 1}], &a)'
order_by
Sort array by multiple fields with ascending/descending control
Signature: array, array[[string, string]] -> array
Examples:
# Multi-field sort
order_by(items, [['name', 'asc'], ['price', 'desc']]) -> sorted
# Single field ascending
order_by(users, [['age', 'asc']]) -> youngest first
# Descending sort
order_by(products, [['price', 'desc']]) -> most expensive first
# Empty array
order_by([], [['a', 'asc']]) -> []
CLI Usage:
echo '{}' | jpx 'order_by(items, [['name', 'asc'], ['price', 'desc']])'
partial
Create a partial function with some arguments pre-filled
Signature: string, ...any -> object
Examples:
# Partial contains
partial('contains', `\"hello\"`) -> {__partial__: true, ...}
# Partial addition
partial('add', `10`) -> partial add 10
# Partial join
partial('join', '-') -> partial join with dash
# Partial multiply
partial('multiply', `2`) -> partial multiply by 2
CLI Usage:
echo '{}' | jpx 'partial(`"contains"`, `\"hello\"`)'
partition_expr
Split array into [matches, non-matches] based on expression
Signature: array, expression -> array
Examples:
# Split by condition
partition_expr([1, 2, 3], &@ > `1`) -> [[2, 3], [1]]
# Even vs odd
partition_expr([1, 2, 3, 4], &@ % `2` == `0`) -> [[2, 4], [1, 3]]
# Partition users
partition_expr(users, &active) -> [active, inactive]
# Empty array
partition_expr([], &@ > `0`) -> [[], []]
CLI Usage:
echo '{}' | jpx 'partition_expr([1, 2, 3], &@ > `1`)'
recurse
Collect all nested values recursively (jq parity)
Signature: any -> array
Examples:
# Nested object
recurse({a: {b: 1}}) -> [{a: {b: 1}}, {b: 1}, 1]
# Nested array
recurse([1, [2, 3]]) -> [[1, [2, 3]], 1, [2, 3], 2, 3]
# Scalar value
recurse(`5`) -> [5]
# All descendants
recurse({a: 1, b: {c: 2}}) -> all nested values
CLI Usage:
echo '{}' | jpx 'recurse({a: {b: 1}})'
recurse_with
Recursive descent with expression filter (jq parity)
Signature: any, expression -> array
Examples:
# Follow 'a' key recursively
recurse_with({a: {a: 1}}, &a) -> [{a: 1}, 1]
# Follow index recursively
recurse_with([1, [2, [3]]], &[1]) -> [[2, [3]], [3]]
# Tree traversal
recurse_with({children: [{children: []}]}, &children) -> nested children
CLI Usage:
echo '{}' | jpx 'recurse_with({a: {a: 1}}, &a)'
reduce_expr
Reduce array to single value using accumulator expression
Signature: string, array, any -> any
Aliases: fold
Examples:
# Sum numbers
reduce_expr('add(accumulator, current)', [1, 2, 3], `0`) -> 6
# Product
reduce_expr('multiply(accumulator, current)', [2, 3, 4], `1`) -> 24
# Find max
reduce_expr('max(accumulator, current)', [3, 1, 4], `0`) -> 4
# Concat strings
reduce_expr('concat(accumulator, current)', ['a', 'b'], '') -> 'ab'
CLI Usage:
echo '{}' | jpx 'reduce_expr(`"add(accumulator, current)"`, [1, 2, 3], `0`)'
reject
Keep elements where expression is falsy (inverse of filter_expr)
Signature: string, array -> array
Examples:
# Reject > 2
reject('@ > `2`', [1, 2, 3, 4]) -> [1, 2]
# Reject negatives
reject('@ < `0`', [1, -1, 2, -2]) -> [1, 2]
# Reject active users
reject('active', users) -> inactive users
# Empty array
reject('@ > `0`', []) -> []
CLI Usage:
echo '{}' | jpx 'reject(`"@ > `2`"`, [1, 2, 3, 4])'
scan_expr
Like reduce but returns array of intermediate accumulator values
Signature: string, array, any -> array
Examples:
# Running sum
scan_expr('add(accumulator, current)', [1, 2, 3], `0`) -> [1, 3, 6]
# Running product
scan_expr('multiply(accumulator, current)', [2, 3, 4], `1`) -> [2, 6, 24]
# Running max
scan_expr('max(accumulator, current)', [3, 1, 4], `0`) -> [3, 3, 4]
# Empty array
scan_expr('add(accumulator, current)', [], `0`) -> []
CLI Usage:
echo '{}' | jpx 'scan_expr(`"add(accumulator, current)"`, [1, 2, 3], `0`)'
some
Check if any element matches (alias for any_expr)
Signature: string, array -> boolean
Examples:
# Some > 2
some('@ > `2`', [1, 2, 3]) -> true
# None > 5
some('@ > `5`', [1, 2, 3]) -> false
# Any active user
some('active', users) -> true/false
# Empty is false
some('@ > `0`', []) -> false
CLI Usage:
echo '{}' | jpx 'some(`"@ > `2`"`, [1, 2, 3])'
sort_by_expr
Sort array by expression result in ascending order
Signature: array, expression -> array
Examples:
# Sort by field
sort_by_expr([{a: 2}, {a: 1}], &a) -> [{a: 1}, {a: 2}]
# Sort by length
sort_by_expr(['bb', 'a', 'ccc'], &length(@)) -> ['a', 'bb', 'ccc']
# Sort users by name
sort_by_expr(users, &name) -> alphabetical
# Empty array
sort_by_expr([], &a) -> []
CLI Usage:
echo '{}' | jpx 'sort_by_expr([{a: 2}, {a: 1}], &a)'
take_while
Take elements from array while expression is truthy
Signature: string, array -> array
Examples:
# Take while < 4
take_while('@ < `4`', [1, 2, 3, 5, 1]) -> [1, 2, 3]
# Take while positive
take_while('@ > `0`', [3, 2, 1, 0, 5]) -> [3, 2, 1]
# Take short strings
take_while('length(@) < `3`', ['a', 'ab', 'abc']) -> ['a', 'ab']
# None taken
take_while('@ < `0`', [1, 2, 3]) -> []
CLI Usage:
echo '{}' | jpx 'take_while(`"@ < `4`"`, [1, 2, 3, 5, 1])'
unique_by_expr
Remove duplicates by expression result, keeping first occurrence
Signature: array, expression -> array
Examples:
# Unique by field
unique_by_expr([{a: 1}, {a: 1}], &a) -> [{a: 1}]
# First wins
unique_by_expr([{id: 1, v: 'a'}, {id: 1, v: 'b'}], &id) -> [{id: 1, v: 'a'}]
# Unique by length
unique_by_expr(['aa', 'b', 'cc'], &length(@)) -> ['aa', 'b']
# Empty array
unique_by_expr([], &a) -> []
CLI Usage:
echo '{}' | jpx 'unique_by_expr([{a: 1}, {a: 1}], &a)'
until_expr
Loop until condition becomes true, collecting intermediate values (jq parity)
Signature: any, expression, expression -> array
Examples:
# Count until >= 5
until_expr(`1`, &@ >= `5`, &add(@, `1`)) -> [1, 2, 3, 4, 5]
# Double until >= 100
until_expr(`2`, &@ >= `100`, &multiply(@, `2`)) -> [2, 4, 8, 16, 32, 64, 128]
# Halve until <= 1
until_expr(`100`, &@ <= `1`, ÷(@, `2`)) -> [100, 50, 25, 12, 6, 3, 1]
CLI Usage:
echo '{}' | jpx 'until_expr(`1`, &@ >= `5`, &add(@, `1`))'
walk
Recursively apply expression to all components of a value (bottom-up)
Signature: string, any -> any
Examples:
# Identity transform
walk('@', data) -> data unchanged
# Get type of result
walk('type(@)', {a: 1}) -> 'object'
# Lengths of nested arrays
walk('length(@)', [[], []]) -> 2
# Keys at each level
walk('keys(@)', {a: {b: 1}}) -> ['a']
CLI Usage:
echo '{}' | jpx 'walk(`"@"`, data)'
while_expr
Loop while condition is true, collecting intermediate values (jq parity)
Signature: any, expression, expression -> array
Examples:
# Count from 1 while < 5
while_expr(`1`, &@ < `5`, &add(@, `1`)) -> [1, 2, 3, 4]
# Double while < 100
while_expr(`2`, &@ < `100`, &multiply(@, `2`)) -> [2, 4, 8, 16, 32, 64]
# Halve while > 1
while_expr(`10`, &@ > `1`, ÷(@, `2`)) -> [10, 5, 2]
CLI Usage:
echo '{}' | jpx 'while_expr(`1`, &@ < `5`, &add(@, `1`))'
zip_with
Zip two arrays with a custom combiner expression
Signature: string, array, array -> array
Examples:
# Add pairs
zip_with('add([0], [1])', [1, 2], [10, 20]) -> [11, 22]
# Multiply pairs
zip_with('multiply([0], [1])', [2, 3], [4, 5]) -> [8, 15]
# Concat pairs
zip_with('concat([0], [1])', ['a', 'b'], ['1', '2']) -> ['a1', 'b2']
# Empty arrays
zip_with('add([0], [1])', [], []) -> []
CLI Usage:
echo '{}' | jpx 'zip_with(`"add([0], [1])"`, [1, 2], [10, 20])'
Type Functions
Type conversion and checking functions.
Summary
| Function | Signature | Description |
|---|---|---|
auto_parse | any -> any | Intelligently parse strings to numbers, booleans, and nulls |
is_array | any -> boolean | Check if value is an array |
is_boolean | any -> boolean | Check if value is a boolean |
is_empty | any -> boolean | Check if value is empty |
is_null | any -> boolean | Check if value is null |
is_number | any -> boolean | Check if value is a number |
is_object | any -> boolean | Check if value is an object |
is_string | any -> boolean | Check if value is a string |
parse_booleans | any -> any | Recursively convert boolean strings to booleans |
parse_nulls | any -> any | Recursively convert null-like strings to null |
parse_numbers | any -> any | Recursively convert numeric strings to numbers |
to_boolean | any -> boolean | Convert value to boolean |
type_of | any -> string | Get the type of a value |
Functions
auto_parse
Intelligently parse strings to numbers, booleans, and nulls
Signature: any -> any
Examples:
# Parse mixed types
auto_parse({num: \"42\", bool: \"true\", nil: \"null\"}) -> {num: 42, bool: true, nil: null}
# Parse array
auto_parse([\"42\", \"true\", \"hello\"]) -> [42, true, \"hello\"]
# Nested parsing
auto_parse({a: {b: \"123\"}}) -> {a: {b: 123}}
CLI Usage:
echo '{}' | jpx 'auto_parse({num: \"42\", bool: \"true\", nil: \"null\"})'
is_array
Check if value is an array
Signature: any -> boolean
Examples:
# Array returns true
is_array([1, 2]) -> true
# String returns false
is_array('hello') -> false
# Object returns false
is_array({}) -> false
# Empty array is array
is_array([]) -> true
CLI Usage:
echo '{}' | jpx 'is_array([1, 2])'
is_boolean
Check if value is a boolean
Signature: any -> boolean
Examples:
# True is boolean
is_boolean(`true`) -> true
# False is boolean
is_boolean(`false`) -> true
# String is not boolean
is_boolean('true') -> false
# Number is not boolean
is_boolean(`1`) -> false
CLI Usage:
echo '{}' | jpx 'is_boolean(`true`)'
is_empty
Check if value is empty
Signature: any -> boolean
Examples:
# Empty array
is_empty([]) -> true
# Empty string
is_empty('') -> true
# Empty object
is_empty({}) -> true
# Non-empty array
is_empty([1, 2]) -> false
CLI Usage:
echo '{}' | jpx 'is_empty([])'
is_null
Check if value is null
Signature: any -> boolean
Examples:
# Null returns true
is_null(`null`) -> true
# Empty string is not null
is_null('') -> false
# Zero is not null
is_null(`0`) -> false
# Missing field is null
is_null(missing_field) -> true
CLI Usage:
echo '{}' | jpx 'is_null(`null`)'
is_number
Check if value is a number
Signature: any -> boolean
Examples:
# Integer is number
is_number(`42`) -> true
# Float is number
is_number(`3.14`) -> true
# String is not number
is_number('42') -> false
# Null is not number
is_number(`null`) -> false
CLI Usage:
echo '{}' | jpx 'is_number(`42`)'
is_object
Check if value is an object
Signature: any -> boolean
Examples:
# Object returns true
is_object({a: 1}) -> true
# Empty object is object
is_object({}) -> true
# Array is not object
is_object([]) -> false
# String is not object
is_object('hello') -> false
CLI Usage:
echo '{}' | jpx 'is_object({a: 1})'
is_string
Check if value is a string
Signature: any -> boolean
Examples:
# String returns true
is_string('hello') -> true
# Empty string is string
is_string('') -> true
# Number is not string
is_string(`42`) -> false
# Array is not string
is_string([]) -> false
CLI Usage:
echo '{}' | jpx 'is_string(`"hello"`)'
parse_booleans
Recursively convert boolean strings to booleans
Signature: any -> any
Examples:
# Parse true
parse_booleans({active: "true"}) -> {active: true}
# Parse yes
parse_booleans({flag: "YES"}) -> {flag: true}
# Parse 1
parse_booleans({enabled: "1"}) -> {enabled: true}
# Multiple booleans
parse_booleans({off: "false", on: "true"}) -> {off: false, on: true}
CLI Usage:
echo '{}' | jpx 'parse_booleans({active: "true"})'
parse_nulls
Recursively convert null-like strings to null
Signature: any -> any
Examples:
# Parse null
parse_nulls({a: "null"}) -> {a: null}
# Parse None
parse_nulls({a: "None"}) -> {a: null}
# Parse nil
parse_nulls({a: "nil"}) -> {a: null}
# Parse undefined
parse_nulls({a: "undefined"}) -> {a: null}
CLI Usage:
echo '{}' | jpx 'parse_nulls({a: "null"})'
parse_numbers
Recursively convert numeric strings to numbers
Signature: any -> any
Examples:
# Parse integer
parse_numbers({count: "42"}) -> {count: 42}
# Parse float
parse_numbers({price: "19.99"}) -> {price: 19.99}
# Nested parsing
parse_numbers({a: {b: "123"}}) -> {a: {b: 123}}
# Non-numeric stays string
parse_numbers({val: "42abc"}) -> {val: "42abc"}
CLI Usage:
echo '{}' | jpx 'parse_numbers({count: "42"})'
to_boolean
Convert value to boolean
Signature: any -> boolean
Examples:
# String 'true'
to_boolean('true') -> true
# String 'false'
to_boolean('false') -> false
# Non-zero is true
to_boolean(`1`) -> true
# Zero is false
to_boolean(`0`) -> false
CLI Usage:
echo '{}' | jpx 'to_boolean(`"true"`)'
type_of
Get the type of a value
Signature: any -> string
Examples:
# Number type
type_of(`42`) -> \"number\"
# String type
type_of('hello') -> \"string\"
# Array type
type_of([1, 2]) -> \"array\"
# Object type
type_of({a: 1}) -> \"object\"
CLI Usage:
echo '{}' | jpx 'type_of(`42`)'
Regular Expression Functions
Regular expression functions: matching, replacing, splitting, and pattern extraction.
Summary
| Function | Signature | Description |
|---|---|---|
regex_extract | string, string -> array | Extract regex matches |
regex_match | string, string -> boolean | Test if string matches regex |
regex_replace | string, string, string -> string | Replace regex matches |
Functions
regex_extract
Extract regex matches
Signature: string, string -> array
Examples:
# Extract numbers
regex_extract('a1b2', '\\\\d+') -> [\"1\", \"2\"]
# Extract words
regex_extract('hello world', '\\\\w+') -> [\"hello\", \"world\"]
# No matches
regex_extract('abc', '\\d+') -> []
CLI Usage:
echo '{}' | jpx 'regex_extract(`"a1b2"`, `"\\\\d+"`)'
regex_match
Test if string matches regex
Signature: string, string -> boolean
Examples:
# Full match
regex_match('hello', '^h.*o$') -> true
# Contains digits
regex_match('test123', '\\d+') -> true
# No match
regex_match('abc', '^\\d+$') -> false
CLI Usage:
echo '{}' | jpx 'regex_match(`"hello"`, `"^h.*o$"`)'
regex_replace
Replace regex matches
Signature: string, string, string -> string
Examples:
# Replace digits
regex_replace('a1b2', '\\\\d+', 'X') -> \"aXbX\"
# Replace spaces
regex_replace('hello world', '\\\\s+', '-') -> \"hello-world\"
# No match unchanged
regex_replace('abc', 'x', 'y') -> \"abc\"
CLI Usage:
echo '{}' | jpx 'regex_replace(`"a1b2"`, `"\\\\d+"`, `"X"`)'
URL Functions
Functions for parsing and manipulating URLs and their components.
Summary
| Function | Signature | Description |
|---|---|---|
url_decode | string -> string | URL decode a string |
url_encode | string -> string | URL encode a string |
url_parse | string -> object | Parse URL into components |
Functions
url_decode
URL decode a string
Signature: string -> string
Examples:
# Decode space
url_decode('hello%20world') -> \"hello world\"
# Decode plus sign
url_decode('a%2Bb') -> \"a+b\"
# Decode percent
url_decode('100%25') -> \"100%\"
# No encoding
url_decode('hello') -> \"hello\"
CLI Usage:
echo '{}' | jpx 'url_decode(`"hello%20world"`)'
url_encode
URL encode a string
Signature: string -> string
Examples:
# Encode space
url_encode('hello world') -> \"hello%20world\"
# Encode plus
url_encode('a+b') -> \"a%2Bb\"
# Encode percent
url_encode('100%') -> \"100%25\"
# No special chars
url_encode('hello') -> \"hello\"
CLI Usage:
echo '{}' | jpx 'url_encode(`"hello world"`)'
url_parse
Parse URL into components
Signature: string -> object
Examples:
# Parse full URL
url_parse('https://example.com/path') -> {scheme: 'https', ...}
# With auth and port
url_parse('http://user:pass@host:8080') -> components
# With query string
url_parse('https://api.example.com/v1?key=val') -> with query
# Relative URL
url_parse('/path/to/file') -> relative path
CLI Usage:
echo '{}' | jpx 'url_parse(`"https://example.com/path"`)'
Semantic Versioning Functions
Semantic versioning functions: parsing, comparing, and manipulating version strings.
Summary
| Function | Signature | Description |
|---|---|---|
semver_compare | string, string -> number | Compare versions (-1, 0, 1) |
semver_is_valid | string -> boolean | Check if string is valid semver |
semver_major | string -> number | Get major version |
semver_minor | string -> number | Get minor version |
semver_parse | string -> object | Parse semantic version |
semver_patch | string -> number | Get patch version |
semver_satisfies | string, string -> boolean | Check if version matches constraint |
Functions
semver_compare
Compare versions (-1, 0, 1)
Signature: string, string -> number
Examples:
# Less than
semver_compare('1.0.0', '2.0.0') -> -1
# Greater than
semver_compare('2.0.0', '1.0.0') -> 1
# Equal
semver_compare('1.0.0', '1.0.0') -> 0
CLI Usage:
echo '{}' | jpx 'semver_compare(`"1.0.0"`, `"2.0.0"`)'
semver_is_valid
Check if string is valid semver
Signature: string -> boolean
Examples:
# Valid semver
semver_is_valid('1.2.3') -> true
# With prerelease
semver_is_valid('1.2.3-alpha') -> true
# Invalid format
semver_is_valid('invalid') -> false
CLI Usage:
echo '{}' | jpx 'semver_is_valid(`"1.2.3"`)'
semver_major
Get major version
Signature: string -> number
Examples:
# Major version
semver_major('1.2.3') -> 1
# Double digit
semver_major('10.0.0') -> 10
# Zero major
semver_major('0.1.0') -> 0
CLI Usage:
echo '{}' | jpx 'semver_major(`"1.2.3"`)'
semver_minor
Get minor version
Signature: string -> number
Examples:
# Minor version
semver_minor('1.2.3') -> 2
# Double digit
semver_minor('1.10.0') -> 10
# Zero minor
semver_minor('1.0.0') -> 0
CLI Usage:
echo '{}' | jpx 'semver_minor(`"1.2.3"`)'
semver_parse
Parse semantic version
Signature: string -> object
Examples:
# Basic version
semver_parse('1.2.3') -> {major: 1, minor: 2, patch: 3}
# With prerelease
semver_parse('1.2.3-alpha').pre -> 'alpha'
CLI Usage:
echo '{}' | jpx 'semver_parse(`"1.2.3"`)'
semver_patch
Get patch version
Signature: string -> number
Examples:
# Patch version
semver_patch('1.2.3') -> 3
# Double digit
semver_patch('1.2.10') -> 10
# Zero patch
semver_patch('1.2.0') -> 0
CLI Usage:
echo '{}' | jpx 'semver_patch(`"1.2.3"`)'
semver_satisfies
Check if version matches constraint
Signature: string, string -> boolean
Examples:
# Caret range
semver_satisfies('1.2.3', '^1.0.0') -> true
# Outside range
semver_satisfies('2.0.0', '^1.0.0') -> false
# Comparison
semver_satisfies('1.5.0', '>=1.0.0') -> true
CLI Usage:
echo '{}' | jpx 'semver_satisfies(`"1.2.3"`, `"^1.0.0"`)'
JSON Patch Functions
JSON Patch (RFC 6902) functions: applying patches, generating diffs, and path operations.
Summary
| Function | Signature | Description |
|---|---|---|
json_diff | object, object -> array | Generate JSON Patch (RFC 6902) that transforms first object into second |
json_merge_patch | object, object -> object | Apply JSON Merge Patch (RFC 7396) to an object |
json_patch | object, array -> object | Apply JSON Patch (RFC 6902) operations to an object |
Functions
json_diff
Generate JSON Patch (RFC 6902) that transforms first object into second
Signature: object, object -> array
Examples:
# Replace value
json_diff({a: 1}, {a: 2}) -> [{op: 'replace', path: '/a', value: 2}]
# Add field
json_diff({a: 1}, {a: 1, b: 2}) -> [{op: 'add', path: '/b', value: 2}]
# Remove field
json_diff({a: 1, b: 2}, {a: 1}) -> [{op: 'remove', path: '/b'}]
CLI Usage:
echo '{}' | jpx 'json_diff({a: 1}, {a: 2})'
json_merge_patch
Apply JSON Merge Patch (RFC 7396) to an object
Signature: object, object -> object
Examples:
# Merge objects
json_merge_patch({a: 1, b: 2}, {b: 3, c: 4}) -> {a: 1, b: 3, c: 4}
# Null removes field
json_merge_patch({a: 1}, {a: `null`}) -> {}
# Add to empty
json_merge_patch({}, {a: 1}) -> {a: 1}
CLI Usage:
echo '{}' | jpx 'json_merge_patch({a: 1, b: 2}, {b: 3, c: 4})'
json_patch
Apply JSON Patch (RFC 6902) operations to an object
Signature: object, array -> object
Examples:
# Add operation
json_patch({a: 1}, [{op: 'add', path: '/b', value: 2}]) -> {a: 1, b: 2}
# Replace operation
json_patch({a: 1}, [{op: 'replace', path: '/a', value: 2}]) -> {a: 2}
# Remove operation
json_patch({a: 1, b: 2}, [{op: 'remove', path: '/b'}]) -> {a: 1}
CLI Usage:
echo '{}' | jpx 'json_patch({a: 1}, [{op: 'add', path: '/b', value: 2}])'
Multi-Match Functions
Functions for matching multiple patterns or expressions in a single operation.
Summary
| Function | Signature | Description |
|---|---|---|
extract_all | string, array[string] -> array[object] | Extract all pattern matches with positions (Aho-Corasick) |
extract_between | string, string, string -> string|null | Extract text between two delimiters |
match_all | string, array[string] -> boolean | Check if string contains all of the patterns (Aho-Corasick) |
match_any | string, array[string] -> boolean | Check if string contains any of the patterns (Aho-Corasick) |
match_count | string, array[string] -> number | Count total pattern matches in string (Aho-Corasick) |
match_positions | string, array[string] -> array[object] | Get start/end positions of all pattern matches (Aho-Corasick) |
match_which | string, array[string] -> array[string] | Return array of patterns that match the string (Aho-Corasick) |
replace_many | string, object -> string | Replace multiple patterns simultaneously (Aho-Corasick) |
split_keep | string, string -> array[string] | Split string keeping delimiters in result |
tokenize | string, object? -> array[string] | Smart word tokenization with optional lowercase and min_length |
Functions
extract_all
Extract all pattern matches with positions (Aho-Corasick)
Signature: string, array[string] -> array[object]
Examples:
# Multiple patterns
extract_all('error warning', ['error', 'warning']) -> [{pattern: 'error', match: 'error', start: 0, end: 5}, ...]
# Overlapping matches
extract_all('abab', ['a', 'b']) -> [{pattern: 'a', match: 'a', start: 0, end: 1}, ...]
CLI Usage:
echo '{}' | jpx 'extract_all(`"error warning"`, ['error', 'warning'])'
extract_between
Extract text between two delimiters
Signature: string, string, string -> string|null
Examples:
# HTML tag content
extract_between('<title>Page</title>', '<title>', '</title>') -> \"Page\"
# Bracketed content
extract_between('Hello [world]!', '[', ']') -> \"world\"
# No delimiters found
extract_between('no match', '[', ']') -> null
CLI Usage:
echo '{}' | jpx 'extract_between(`"<title>Page</title>"`, `"<title>"`, `"</title>"`)'
match_all
Check if string contains all of the patterns (Aho-Corasick)
Signature: string, array[string] -> boolean
Examples:
# All patterns found
match_all('hello world', ['hello', 'world']) -> true
# Missing pattern
match_all('hello world', ['hello', 'foo']) -> false
# Empty patterns
match_all('abc', []) -> true
CLI Usage:
echo '{}' | jpx 'match_all(`"hello world"`, ['hello', 'world'])'
match_any
Check if string contains any of the patterns (Aho-Corasick)
Signature: string, array[string] -> boolean
Examples:
# One pattern found
match_any('hello world', ['world', 'foo']) -> true
# No patterns found
match_any('hello world', ['foo', 'bar']) -> false
# Empty patterns
match_any('abc', []) -> false
CLI Usage:
echo '{}' | jpx 'match_any(`"hello world"`, ['world', 'foo'])'
match_count
Count total pattern matches in string (Aho-Corasick)
Signature: string, array[string] -> number
Examples:
# Count all matches
match_count('abcabc', ['a', 'b']) -> 4
# Repeated pattern
match_count('hello', ['l']) -> 2
# No matches
match_count('abc', ['x']) -> 0
CLI Usage:
echo '{}' | jpx 'match_count(`"abcabc"`, ['a', 'b'])'
match_positions
Get start/end positions of all pattern matches (Aho-Corasick)
Signature: string, array[string] -> array[object]
Examples:
# Find positions
match_positions('The quick fox', ['quick', 'fox']) -> [{pattern: 'quick', start: 4, end: 9}, ...]
# Multiple occurrences
match_positions('abab', ['ab']) -> [{pattern: 'ab', start: 0, end: 2}, {pattern: 'ab', start: 2, end: 4}]
CLI Usage:
echo '{}' | jpx 'match_positions(`"The quick fox"`, ['quick', 'fox'])'
match_which
Return array of patterns that match the string (Aho-Corasick)
Signature: string, array[string] -> array[string]
Examples:
# Find matching patterns
match_which('hello world', ['hello', 'foo', 'world']) -> [\"hello\", \"world\"]
# Partial matches
match_which('abc', ['a', 'b', 'x']) -> [\"a\", \"b\"]
# No matches
match_which('abc', ['x', 'y']) -> []
CLI Usage:
echo '{}' | jpx 'match_which(`"hello world"`, ['hello', `"foo"`, 'world'])'
replace_many
Replace multiple patterns simultaneously (Aho-Corasick)
Signature: string, object -> string
Examples:
# Multiple replacements
replace_many('hello world', {hello: 'hi', world: 'earth'}) -> \"hi earth\"
# Repeated pattern
replace_many('aaa', {a: 'b'}) -> \"bbb\"
# No matches
replace_many('abc', {x: 'y'}) -> \"abc\"
CLI Usage:
echo '{}' | jpx 'replace_many(`"hello world"`, {hello: 'hi', world: 'earth'})'
split_keep
Split string keeping delimiters in result
Signature: string, string -> array[string]
Examples:
# Keep dashes
split_keep('a-b-c', '-') -> [\"a\", \"-\", \"b\", \"-\", \"c\"]
# Keep spaces
split_keep('hello world', ' ') -> [\"hello\", \" \", \"world\"]
# No delimiter
split_keep('abc', '-') -> [\"abc\"]
CLI Usage:
echo '{}' | jpx 'split_keep(`"a-b-c"`, `"-"`)'
tokenize
Smart word tokenization with optional lowercase and min_length
Signature: string, object? -> array[string]
Examples:
# Basic tokenization
tokenize('Hello, World!') -> [\"Hello\", \"World\"]
# Lowercase option
tokenize('Hello, World!', {lowercase: `true`}) -> [\"hello\", \"world\"]
# Minimum length
tokenize('a bb ccc', {min_length: `2`}) -> [\"bb\", \"ccc\"]
CLI Usage:
echo '{}' | jpx 'tokenize(`"Hello, World!"`)'
UUID Functions
Functions for generating and working with UUIDs.
Summary
| Function | Signature | Description |
|---|---|---|
uuid | -> string | Generate a UUID v4 |
Functions
uuid
Generate a UUID v4
Signature: -> string
Examples:
# Generate UUID
uuid() -> \"550e8400-e29b-41d4-a716-446655440000\"
# Each call is unique
uuid() -> random unique ID
# Add ID to object
{id: uuid(), name: 'item'} -> with UUID
CLI Usage:
echo '{}' | jpx 'uuid()'
ID Generation Functions
Functions for generating various types of unique identifiers.
Summary
| Function | Signature | Description |
|---|---|---|
nanoid | number? -> string | Generate nanoid |
ulid | -> string | Generate ULID |
ulid_timestamp | string -> number | Extract timestamp from ULID |
Functions
nanoid
Generate nanoid
Signature: number? -> string
Examples:
# Default 21 chars
nanoid() -> \"V1StGXR8_Z5jdHi6B-myT\"
# Custom length
nanoid(`10`) -> \"IRFa-VaY2b\"
CLI Usage:
echo '{}' | jpx 'nanoid()'
ulid
Generate ULID
Signature: -> string
Examples:
# Generate ULID
ulid() -> \"01ARZ3NDEKTSV4RRFFQ69G5FAV\"
# Always 26 chars
length(ulid()) -> 26
CLI Usage:
echo '{}' | jpx 'ulid()'
ulid_timestamp
Extract timestamp from ULID
Signature: string -> number
Examples:
# Extract timestamp
ulid_timestamp('01ARZ3NDEKTSV4RRFFQ69G5FAV') -> 1469918176385
# Another ULID
ulid_timestamp('01BX5ZZKBKACTAV9WEVGEMMVRY') -> 1484581420610
CLI Usage:
echo '{}' | jpx 'ulid_timestamp(`"01ARZ3NDEKTSV4RRFFQ69G5FAV"`)'
Random Functions
Functions for generating random values: numbers, strings, and selections.
Summary
| Function | Signature | Description |
|---|---|---|
random | -> number | Generate random number between 0 and 1 |
sample | array, number -> array | Random sample from array |
shuffle | array -> array | Randomly shuffle array |
Functions
random
Generate random number between 0 and 1
Signature: -> number
Examples:
# Random float
random() -> 0.123456...
# Random 0-99
floor(multiply(random(), `100`)) -> 42
CLI Usage:
echo '{}' | jpx 'random()'
sample
Random sample from array
Signature: array, number -> array
Examples:
# Sample 2 items
sample([1, 2, 3, 4], `2`) -> [3, 1]
# Sample 1 item
sample(['a', 'b', 'c'], `1`) -> ['b']
# Always returns n items
length(sample([1, 2, 3], `2`)) -> 2
CLI Usage:
echo '{}' | jpx 'sample([1, 2, 3, 4], `2`)'
shuffle
Randomly shuffle array
Signature: array -> array
Examples:
# Shuffle numbers
shuffle([1, 2, 3]) -> [2, 3, 1]
# Preserves length
length(shuffle([1, 2, 3])) -> 3
CLI Usage:
echo '{}' | jpx 'shuffle([1, 2, 3])'
Text Functions
Text analysis and processing functions for NLP pipelines.
Summary
| Function | Signature | Description |
|---|---|---|
bigrams | string -> array | Generate word bigrams (2-grams) |
char_count | string -> number | Count characters in text |
char_frequencies | string -> object | Count character frequencies |
collapse_whitespace | string -> string | Normalize whitespace |
is_stopword | string, string? -> boolean | Check if word is a stopword |
ngrams | string, number, string? -> array | Generate n-grams from text (word or character) |
normalize_unicode | string, string? -> string | Unicode normalization (NFC/NFD/NFKC/NFKD) |
paragraph_count | string -> number | Count paragraphs in text |
reading_time | string -> string | Estimate reading time |
reading_time_seconds | string -> number | Estimate reading time in seconds |
remove_accents | string -> string | Strip diacritics from text |
remove_stopwords | array, string? -> array | Filter stopwords from token array |
sentence_count | string -> number | Count sentences in text |
stem | string, string? -> string | Stem a word (17 languages) |
stems | array, string? -> array | Stem array of tokens |
stopwords | string? -> array | Get stopword list for language |
tokenize | string, object? -> array | Configurable tokenization |
tokens | string -> array | Simple word tokenization |
trigrams | string -> array | Generate word trigrams (3-grams) |
word_count | string -> number | Count words in text |
word_frequencies | string -> object | Count word frequencies |
Functions
bigrams
Generate word bigrams (2-grams)
Signature: string -> array
Examples:
# Basic bigrams
bigrams('a b c') -> [['a', 'b'], ['b', 'c']]
# Sentence bigrams
bigrams('the quick brown fox') -> [['the', 'quick'], ['quick', 'brown'], ['brown', 'fox']]
# Single word
bigrams('single') -> []
CLI Usage:
echo '{}' | jpx "bigrams('a b c')"
char_count
Count characters in text
Signature: string -> number
Examples:
# Simple word
char_count('hello') -> 5
# With space
char_count('hello world') -> 11
# Empty string
char_count('') -> 0
CLI Usage:
echo '{}' | jpx "char_count('hello')"
char_frequencies
Count character frequencies
Signature: string -> object
Examples:
# Count repeated chars
char_frequencies('aab') -> {a: 2, b: 1}
# Word frequencies
char_frequencies('hello') -> {e: 1, h: 1, l: 2, o: 1}
# Empty string
char_frequencies('') -> {}
CLI Usage:
echo '{}' | jpx "char_frequencies('aab')"
collapse_whitespace
Normalize whitespace by collapsing multiple spaces, tabs, and newlines into single spaces.
Signature: string -> string
Examples:
# Multiple spaces
collapse_whitespace('hello world') -> 'hello world'
# Tabs and newlines
collapse_whitespace('a\t\nb') -> 'a b'
# Leading/trailing whitespace
collapse_whitespace(' hello ') -> 'hello'
CLI Usage:
echo '"hello world"' | jpx 'collapse_whitespace(@)'
is_stopword
Check if a word is a stopword in the specified language.
Signature: string, string? -> boolean
Parameters:
word- The word to checklang- Language code (default: “en”). Supports 30+ languages.
Examples:
# English stopword
is_stopword('the') -> true
# Not a stopword
is_stopword('algorithm') -> false
# Spanish
is_stopword('el', 'es') -> true
# German
is_stopword('und', 'de') -> true
CLI Usage:
echo '{}' | jpx "is_stopword('the')"
ngrams
Generate n-grams from text (word or character)
Signature: string, number, string? -> array
Examples:
# Character trigrams
ngrams('hello', `3`, 'char') -> ['hel', 'ell', 'llo']
# Word bigrams
ngrams('a b c d', `2`, 'word') -> [['a', 'b'], ['b', 'c'], ['c', 'd']]
# Text shorter than n
ngrams('ab', `3`, 'char') -> []
CLI Usage:
echo '{}' | jpx "ngrams('hello', \`3\`, 'char')"
normalize_unicode
Apply Unicode normalization to text.
Signature: string, string? -> string
Parameters:
text- The text to normalizeform- Normalization form: “nfc” (default), “nfd”, “nfkc”, or “nfkd”
Examples:
# NFC (composed, default)
normalize_unicode('café') -> 'café'
# NFD (decomposed)
normalize_unicode('é', 'nfd') -> 'é' # e + combining acute
# NFKC (compatibility composed)
normalize_unicode('fi', 'nfkc') -> 'fi'
CLI Usage:
echo '"café"' | jpx "normalize_unicode(@)"
paragraph_count
Count paragraphs in text
Signature: string -> number
Examples:
# Two paragraphs
paragraph_count('A\n\nB') -> 2
# Single paragraph
paragraph_count('Single paragraph') -> 1
# Three paragraphs
paragraph_count('A\n\nB\n\nC') -> 3
CLI Usage:
echo '{}' | jpx "paragraph_count('A\n\nB')"
reading_time
Estimate reading time
Signature: string -> string
Examples:
# Short text
reading_time('The quick brown fox') -> "1 min read"
# Empty text minimum
reading_time('') -> "1 min read"
CLI Usage:
echo '{}' | jpx "reading_time('The quick brown fox')"
reading_time_seconds
Estimate reading time in seconds
Signature: string -> number
Examples:
# Short sentence
reading_time_seconds('The quick brown fox jumps over the lazy dog') -> 2
# Empty text
reading_time_seconds('') -> 0
CLI Usage:
echo '{}' | jpx "reading_time_seconds('The quick brown fox jumps over the lazy dog')"
remove_accents
Strip diacritical marks (accents) from text.
Signature: string -> string
Examples:
# French
remove_accents('café') -> 'cafe'
# Spanish
remove_accents('señor') -> 'senor'
# German
remove_accents('über') -> 'uber'
# Mixed
remove_accents('naïve résumé') -> 'naive resume'
CLI Usage:
echo '"café"' | jpx 'remove_accents(@)'
remove_stopwords
Filter stopwords from an array of tokens.
Signature: array, string? -> array
Parameters:
tokens- Array of word tokenslang- Language code (default: “en”)
Examples:
# English
remove_stopwords(['the', 'quick', 'brown', 'fox']) -> ['quick', 'brown', 'fox']
# Complete sentence
remove_stopwords(['i', 'am', 'learning', 'rust']) -> ['learning', 'rust']
# Spanish
remove_stopwords(['el', 'gato', 'negro'], 'es') -> ['gato', 'negro']
CLI Usage:
echo '["the", "quick", "brown", "fox"]' | jpx "remove_stopwords(@)"
sentence_count
Count sentences in text
Signature: string -> number
Examples:
# Two sentences
sentence_count('Hello. World!') -> 2
# Single sentence
sentence_count('One sentence') -> 1
# Different terminators
sentence_count('What? Yes! No.') -> 3
CLI Usage:
echo '{}' | jpx "sentence_count('Hello. World!')"
stem
Stem a single word using the Snowball stemmer.
Signature: string, string? -> string
Parameters:
word- The word to stemlang- Language code (default: “en”). Supports 17 languages: ar, da, de, el, en, es, fi, fr, hu, it, nl, no, pt, ro, ru, sv, tr.
Examples:
# English
stem('running') -> 'run'
stem('connections') -> 'connect'
stem('happiness') -> 'happi'
# Spanish
stem('corriendo', 'es') -> 'corr'
# German
stem('laufend', 'de') -> 'lauf'
CLI Usage:
echo '{}' | jpx "stem('running')"
stems
Stem an array of tokens.
Signature: array, string? -> array
Parameters:
tokens- Array of word tokenslang- Language code (default: “en”)
Examples:
# Stem multiple words
stems(['running', 'jumps', 'walked']) -> ['run', 'jump', 'walk']
# Spanish
stems(['corriendo', 'saltando'], 'es') -> ['corr', 'salt']
CLI Usage:
echo '["running", "jumps", "walked"]' | jpx "stems(@)"
stopwords
Get the list of stopwords for a language.
Signature: string? -> array
Parameters:
lang- Language code (default: “en”). Supports 30+ languages.
Examples:
# English stopwords (first few)
stopwords() | slice(@, `0`, `5`) -> ['i', 'me', 'my', 'myself', 'we']
# Check count
stopwords() | length(@) -> 179
# Spanish
stopwords('es') | length(@) -> 313
# German
stopwords('de') | slice(@, `0`, `3`) -> ['aber', 'alle', 'allem']
CLI Usage:
echo '{}' | jpx "stopwords() | length(@)"
tokenize
Tokenize text with configurable options.
Signature: string, object? -> array
Parameters:
text- The text to tokenizeoptions- Optional configuration object:case: “lower” (default) or “preserve”punctuation: “strip” (default) or “keep”
Examples:
# Default (lowercase, strip punctuation)
tokenize('Hello, World!') -> ['hello', 'world']
# Preserve case
tokenize('Hello World', `{"case": "preserve"}`) -> ['Hello', 'World']
# Keep punctuation
tokenize('Hello, World!', `{"punctuation": "keep"}`) -> ['hello,', 'world!']
# Both options
tokenize('Hello!', `{"case": "preserve", "punctuation": "keep"}`) -> ['Hello!']
CLI Usage:
echo '"Hello, World!"' | jpx 'tokenize(@)'
tokens
Simple word tokenization (normalized, lowercase, punctuation stripped).
Signature: string -> array
Examples:
# Basic tokenization
tokens('Hello, World!') -> ['hello', 'world']
# Multiple words
tokens('The quick brown fox') -> ['the', 'quick', 'brown', 'fox']
# Numbers included
tokens('Test 123') -> ['test', '123']
CLI Usage:
echo '"Hello, World!"' | jpx 'tokens(@)'
trigrams
Generate word trigrams (3-grams)
Signature: string -> array
Examples:
# Basic trigrams
trigrams('a b c d') -> [['a', 'b', 'c'], ['b', 'c', 'd']]
# Sentence trigrams
trigrams('the quick brown fox jumps') -> [['the', 'quick', 'brown'], ['quick', 'brown', 'fox'], ['brown', 'fox', 'jumps']]
# Too few words
trigrams('a b') -> []
CLI Usage:
echo '{}' | jpx "trigrams('a b c d')"
word_count
Count words in text
Signature: string -> number
Examples:
# Two words
word_count('hello world') -> 2
# Single word
word_count('one') -> 1
# Empty string
word_count('') -> 0
CLI Usage:
echo '{}' | jpx "word_count('hello world')"
word_frequencies
Count word frequencies
Signature: string -> object
Examples:
# Count repeated words
word_frequencies('a a b') -> {a: 2, b: 1}
# Unique words
word_frequencies('the quick brown fox') -> {brown: 1, fox: 1, quick: 1, the: 1}
# Empty string
word_frequencies('') -> {}
CLI Usage:
echo '{}' | jpx "word_frequencies('a a b')"
Language Functions
Natural language processing functions.
Summary
| Function | Signature | Description |
|---|---|---|
detect_language | string -> string | null | Detect the language of text, returning the native language name |
detect_language_confidence | string -> number | null | Detect language and return confidence score (0.0-1.0) |
detect_language_info | string -> object | null | Detect language and return full detection info object |
detect_language_iso | string -> string | null | Detect the language of text, returning the ISO 639-3 code |
detect_script | string -> string | null | Detect the script (writing system) of text |
Functions
detect_language
Detect the language of text, returning the native language name
Signature: string -> string | null
Examples:
# Detect English
detect_language('This is English text.') -> \"English\"
# Detect Spanish (native name)
detect_language('Esto es texto en español.') -> \"Español\"
# Detect French (native name)
detect_language('Ceci est du texte français.') -> \"Français\"
# Empty or undetectable returns null
detect_language('') -> null
CLI Usage:
echo '{}' | jpx 'detect_language(`"This is English text."`)'
detect_language_confidence
Detect language and return confidence score (0.0-1.0)
Signature: string -> number | null
Examples:
# High confidence
detect_language_confidence('This is definitely English text.') -> 0.95
# Low confidence for short text
detect_language_confidence('Hi') -> 0.3
# Empty returns null
detect_language_confidence('') -> null
CLI Usage:
echo '{}' | jpx 'detect_language_confidence(`"This is definitely English text."`)'
detect_language_info
Detect language and return full detection info object
Signature: string -> object | null
Examples:
# Full detection info
detect_language_info('This is a test.') -> {language: 'English', code: 'eng', script: 'Latin', confidence: 0.9, reliable: true}
# Access language field
detect_language_info('Bonjour').language -> 'French'
# Check reliability
detect_language_info('Hola').reliable -> true/false
# Empty returns null
detect_language_info('') -> null
CLI Usage:
echo '{}' | jpx 'detect_language_info(`"This is a test."`)'
detect_language_iso
Detect the language of text, returning the ISO 639-3 code
Signature: string -> string | null
Examples:
# English ISO code
detect_language_iso('This is English text.') -> \"eng\"
# Spanish ISO code
detect_language_iso('Esto es texto en español.') -> \"spa\"
# German ISO code
detect_language_iso('Dies ist deutscher Text.') -> \"deu\"
# Empty or undetectable returns null
detect_language_iso('') -> null
CLI Usage:
echo '{}' | jpx 'detect_language_iso(`"This is English text."`)'
detect_script
Detect the script (writing system) of text
Signature: string -> string | null
Examples:
# Latin script
detect_script('Hello world') -> \"Latin\"
# Cyrillic script
detect_script('Привет мир') -> \"Cyrillic\"
# Arabic script
detect_script('مرحبا بالعالم') -> \"Arabic\"
# Japanese Hiragana
detect_script('こんにちは') -> \"Hiragana\"
CLI Usage:
echo '{}' | jpx 'detect_script(`"Hello world"`)'
Fuzzy Functions
Fuzzy matching and string similarity functions.
Summary
| Function | Signature | Description |
|---|---|---|
damerau_levenshtein | string, string -> number | Damerau-Levenshtein distance |
hamming | string, string -> number|null | Hamming distance (number of differing positions). Returns null if strings have different lengths |
jaro | string, string -> number | Jaro similarity (0-1) |
jaro_winkler | string, string -> number | Jaro-Winkler similarity (0-1) |
levenshtein | string, string -> number | Levenshtein edit distance |
normalized_damerau_levenshtein | string, string -> number | Normalized Damerau-Levenshtein similarity (0-1) |
normalized_levenshtein | string, string -> number | Normalized Levenshtein (0-1) |
osa_distance | string, string -> number | Optimal String Alignment distance (like Levenshtein but allows adjacent transpositions) |
sorensen_dice | string, string -> number | Sorensen-Dice coefficient (0-1) |
Functions
damerau_levenshtein
Damerau-Levenshtein distance
Signature: string, string -> number
Examples:
# Single transposition
damerau_levenshtein('ab', 'ba') -> 1
# Identical strings
damerau_levenshtein('hello', 'hello') -> 0
# Multiple edits
damerau_levenshtein('ca', 'abc') -> 2
CLI Usage:
echo '{}' | jpx 'damerau_levenshtein(`"ab"`, `"ba"`)'
hamming
Hamming distance (number of differing positions). Returns null if strings have different lengths
Signature: string, string -> number|null
Examples:
# Three differing positions
hamming('karolin', 'kathrin') -> 3
# Identical strings
hamming('hello', 'hello') -> 0
# Different lengths
hamming('hello', 'hi') -> null
CLI Usage:
echo '{}' | jpx 'hamming(`"karolin"`, `"kathrin"`)'
jaro
Jaro similarity (0-1)
Signature: string, string -> number
Examples:
# Similar words
jaro('hello', 'hallo') -> 0.866...
# Identical strings
jaro('hello', 'hello') -> 1.0
# Completely different
jaro('abc', 'xyz') -> 0.0
CLI Usage:
echo '{}' | jpx 'jaro(`"hello"`, `"hallo"`)'
jaro_winkler
Jaro-Winkler similarity (0-1)
Signature: string, string -> number
Examples:
# Similar words
jaro_winkler('hello', 'hallo') -> 0.88
# Identical strings
jaro_winkler('hello', 'hello') -> 1.0
# Completely different
jaro_winkler('abc', 'xyz') -> 0.0
CLI Usage:
echo '{}' | jpx 'jaro_winkler(`"hello"`, `"hallo"`)'
levenshtein
Levenshtein edit distance
Signature: string, string -> number
Examples:
# Classic example
levenshtein('kitten', 'sitting') -> 3
# Identical strings
levenshtein('hello', 'hello') -> 0
# All different
levenshtein('abc', 'def') -> 3
CLI Usage:
echo '{}' | jpx 'levenshtein(`"kitten"`, `"sitting"`)'
normalized_damerau_levenshtein
Normalized Damerau-Levenshtein similarity (0-1)
Signature: string, string -> number
Examples:
# Identical strings
normalized_damerau_levenshtein('hello', 'hello') -> 1.0
# Transposition
normalized_damerau_levenshtein('ab', 'ba') -> 0.5
# Completely different
normalized_damerau_levenshtein('abc', 'xyz') -> 0.0
CLI Usage:
echo '{}' | jpx 'normalized_damerau_levenshtein(`"hello"`, `"hello"`)'
normalized_levenshtein
Normalized Levenshtein (0-1)
Signature: string, string -> number
Examples:
# One edit
normalized_levenshtein('ab', 'abc') -> 0.666...
# Identical
normalized_levenshtein('hello', 'hello') -> 0.0
# All different
normalized_levenshtein('abc', 'xyz') -> 1.0
CLI Usage:
echo '{}' | jpx 'normalized_levenshtein(`"ab"`, `"abc"`)'
osa_distance
Optimal String Alignment distance (like Levenshtein but allows adjacent transpositions)
Signature: string, string -> number
Examples:
# Single transposition
osa_distance('ab', 'ba') -> 1
# Identical strings
osa_distance('hello', 'hello') -> 0
# Multiple edits
osa_distance('ca', 'abc') -> 3
CLI Usage:
echo '{}' | jpx 'osa_distance(`"ab"`, `"ba"`)'
sorensen_dice
Sorensen-Dice coefficient (0-1)
Signature: string, string -> number
Examples:
# Similar words
sorensen_dice('night', 'nacht') -> 0.25
# Identical strings
sorensen_dice('hello', 'hello') -> 1.0
# No common bigrams
sorensen_dice('abc', 'xyz') -> 0.0
CLI Usage:
echo '{}' | jpx 'sorensen_dice(`"night"`, `"nacht"`)'
Discovery Functions
Functions for searching and matching within collections, useful for tool discovery and fuzzy filtering.
Summary
| Function | Signature | Description |
|---|---|---|
fuzzy_search | array, string|object, string -> array | Search array of objects by multiple fields with relevance scoring |
fuzzy_match | string, string -> object | Check if a string matches a query, with match details |
fuzzy_score | string, string -> number | Get numeric match score between value and query |
Functions
fuzzy_search
Search an array of objects by multiple fields, returning matches sorted by relevance score.
Signature: array, string|object, string -> array
The second argument specifies which fields to search:
- String: Comma-separated field names with equal weight:
"name,description" - Object: Field names with custom weights:
`{"name": 10, "description": 5}`
Examples:
# Search tools by name and description fields
fuzzy_search(tools, `"name,description"`, `"user"`)
# -> [{item: {...}, score: 100, match_type: "exact", matched_field: "name"}, ...]
# Search with weighted fields (name matches worth more)
fuzzy_search(tools, `{"name": 10, "description": 5}`, `"cache"`)
# Get the name of the top match
fuzzy_search(tools, `"name,tags"`, `"redis"`)[0].item.name
CLI Usage:
# Search a list of tools for "backup"
cat tools.json | jpx 'fuzzy_search(@, `"name,description"`, `"backup"`)'
Return format:
Each result contains:
item: The original object that matchedscore: Relevance score (higher = better match)match_type: How it matched ("exact","prefix","contains","fuzzy")matched_field: Which field produced the match
fuzzy_match
Check if a single string value matches a query, returning detailed match information.
Signature: string, string -> object
Match types (in priority order):
- exact (score: 1000): Exact match after normalization
- prefix (score: 800): Query is a prefix of the value
- contains (score: 600): Query appears within the value
- fuzzy (score: varies): Similarity-based match
- none (score: 0): No meaningful match
Examples:
# Exact match
fuzzy_match(`"get_user"`, `"get_user"`)
# -> {matches: true, score: 1000, match_type: "exact"}
# Prefix match
fuzzy_match(`"get_user_info"`, `"get"`)
# -> {matches: true, score: 800, match_type: "prefix"}
# Contains match
fuzzy_match(`"hello_world"`, `"world"`)
# -> {matches: true, score: 600, match_type: "contains"}
# No match (returns similarity score for near-misses)
fuzzy_match(`"hello"`, `"xyz"`)
# -> {matches: false, score: 0, match_type: "none", similarity: 0.0}
CLI Usage:
echo '{}' | jpx 'fuzzy_match(`"database_backup"`, `"backup"`)'
fuzzy_score
Get the numeric match score between a value and query. This is a simpler alternative to fuzzy_match when you only need the score.
Signature: string, string -> number
Score ranges:
- 1000: Exact match
- 800: Prefix match
- 600: Contains match
- 1-599: Fuzzy similarity match
- 0: No match
Examples:
# Exact match - highest score
fuzzy_score(`"hello"`, `"hello"`) -> 1000
# Prefix match
fuzzy_score(`"hello_world"`, `"hello"`) -> 800
# Contains match
fuzzy_score(`"say_hello"`, `"hello"`) -> 600
# No match
fuzzy_score(`"hello"`, `"goodbye"`) -> 0
CLI Usage:
# Score a potential match
echo '{}' | jpx 'fuzzy_score(`"enterprise_database_create"`, `"database"`)'
Use with filtering:
# Filter to items with score > 500
tools[?fuzzy_score(name, `"user"`) > `500`]
# Sort by match quality
sort_by(tools, &fuzzy_score(name, `"cache"`)) | reverse(@)
Use Cases
Tool Discovery
Find relevant tools from a large list:
fuzzy_search(mcp_tools, `"name,description"`, `"backup database"`)
| [?score > `100`]
| [:5]
| [*].{name: item.name, score: score, why: match_type}
Autocomplete
Filter options as user types:
commands[?fuzzy_score(name, query) > `0`]
| sort_by(@, &fuzzy_score(name, query))
| reverse(@)
| [:10]
Deduplication
Find similar entries:
items[?fuzzy_match(title, other_title).match_type == `"fuzzy"`
&& fuzzy_match(title, other_title).score > `400`]
Phonetic Functions
Phonetic encoding functions for sound-based string matching.
Summary
| Function | Signature | Description |
|---|---|---|
caverphone | string -> string | Caverphone code |
caverphone2 | string -> string | Caverphone 2 code |
double_metaphone | string -> object | Double Metaphone codes |
match_rating_codex | string -> string | Match Rating codex |
metaphone | string -> string | Metaphone phonetic code |
nysiis | string -> string | NYSIIS phonetic code |
phonetic_match | string, string, string -> boolean | Check phonetic match with algorithm |
soundex | string -> string | Soundex phonetic code |
sounds_like | string, string -> boolean | Check if strings sound similar |
Functions
caverphone
Caverphone code
Signature: string -> string
Examples:
# Common name
caverphone('Smith') -> \"SMT1111111\"
# Another name
caverphone('Thompson') -> \"TMPSN11111\"
CLI Usage:
echo '{}' | jpx 'caverphone(`"Smith"`)'
caverphone2
Caverphone 2 code
Signature: string -> string
Examples:
# Common name
caverphone2('Smith') -> \"SMT1111111\"
# Another name
caverphone2('Thompson') -> \"TMPSN11111\"
CLI Usage:
echo '{}' | jpx 'caverphone2(`"Smith"`)'
double_metaphone
Double Metaphone codes
Signature: string -> object
Examples:
# Common name
double_metaphone('Smith') -> {primary: 'SM0', secondary: 'XMT'}
# German variant
double_metaphone('Schmidt') -> {primary: 'XMT', secondary: 'SMT'}
CLI Usage:
echo '{}' | jpx 'double_metaphone(`"Smith"`)'
match_rating_codex
Match Rating codex
Signature: string -> string
Examples:
# Common name
match_rating_codex('Smith') -> \"SMTH\"
# Another name
match_rating_codex('Johnson') -> \"JHNSN\"
CLI Usage:
echo '{}' | jpx 'match_rating_codex(`"Smith"`)'
metaphone
Metaphone phonetic code
Signature: string -> string
Examples:
# Common name
metaphone('Smith') -> \"SM0\"
# Ph sound
metaphone('phone') -> \"FN\"
CLI Usage:
echo '{}' | jpx 'metaphone(`"Smith"`)'
nysiis
NYSIIS phonetic code
Signature: string -> string
Examples:
# Common name
nysiis('Smith') -> \"SNAT\"
# Another name
nysiis('Johnson') -> \"JANSAN\"
CLI Usage:
echo '{}' | jpx 'nysiis(`"Smith"`)'
phonetic_match
Check phonetic match with algorithm
Signature: string, string, string -> boolean
Examples:
# Soundex match
phonetic_match('Smith', 'Smyth', 'soundex') -> true
# Metaphone match
phonetic_match('Robert', 'Rupert', 'metaphone') -> true
# No match
phonetic_match('John', 'Jane', 'soundex') -> false
CLI Usage:
echo '{}' | jpx 'phonetic_match(`"Smith"`, `"Smyth"`, `"soundex"`)'
soundex
Soundex phonetic code
Signature: string -> string
Examples:
# Common name
soundex('Robert') -> \"R163\"
# Same code as Robert
soundex('Rupert') -> \"R163\"
# Another name
soundex('Smith') -> \"S530\"
CLI Usage:
echo '{}' | jpx 'soundex(`"Robert"`)'
sounds_like
Check if strings sound similar
Signature: string, string -> boolean
Examples:
# Similar sounding
sounds_like('Robert', 'Rupert') -> true
# Spelling variants
sounds_like('Smith', 'Smyth') -> true
# Different names
sounds_like('John', 'Mary') -> false
CLI Usage:
echo '{}' | jpx 'sounds_like(`"Robert"`, `"Rupert"`)'
Network Functions
Network-related functions: IP addresses, CIDR notation, and network utilities.
Summary
| Function | Signature | Description |
|---|---|---|
cidr_broadcast | string -> string | Get broadcast address from CIDR |
cidr_contains | string, string -> boolean | Check if IP is in CIDR range |
cidr_network | string -> string | Get network address from CIDR |
cidr_prefix | string -> number | Get prefix length from CIDR |
int_to_ip | number -> string | Convert integer to IP address |
ip_to_int | string -> number | Convert IP address to integer |
is_private_ip | string -> boolean | Check if IP is in private range |
Functions
cidr_broadcast
Get broadcast address from CIDR
Signature: string -> string
Examples:
# /24 network
cidr_broadcast('192.168.1.0/24') -> \"192.168.1.255\"
# /8 network
cidr_broadcast('10.0.0.0/8') -> \"10.255.255.255\"
# /16 network
cidr_broadcast('172.16.0.0/16') -> \"172.16.255.255\"
CLI Usage:
echo '{}' | jpx 'cidr_broadcast(`"192.168.1.0/24"`)'
cidr_contains
Check if IP is in CIDR range
Signature: string, string -> boolean
Examples:
# IP in range
cidr_contains('192.168.0.0/16', '192.168.1.1') -> true
# IP not in range
cidr_contains('10.0.0.0/8', '192.168.1.1') -> false
# Catch-all range
cidr_contains('0.0.0.0/0', '8.8.8.8') -> true
CLI Usage:
echo '{}' | jpx 'cidr_contains(`"192.168.0.0/16"`, `"192.168.1.1"`)'
cidr_network
Get network address from CIDR
Signature: string -> string
Examples:
# /24 network
cidr_network('192.168.1.0/24') -> \"192.168.1.0\"
# /8 network
cidr_network('10.0.0.0/8') -> \"10.0.0.0\"
# Host in network
cidr_network('192.168.1.100/24') -> \"192.168.1.0\"
CLI Usage:
echo '{}' | jpx 'cidr_network(`"192.168.1.0/24"`)'
cidr_prefix
Get prefix length from CIDR
Signature: string -> number
Examples:
# /24 prefix
cidr_prefix('192.168.1.0/24') -> 24
# /8 prefix
cidr_prefix('10.0.0.0/8') -> 8
# Default route
cidr_prefix('0.0.0.0/0') -> 0
CLI Usage:
echo '{}' | jpx 'cidr_prefix(`"192.168.1.0/24"`)'
int_to_ip
Convert integer to IP address
Signature: number -> string
Examples:
# Private IP
int_to_ip(`3232235777`) -> \"192.168.1.1\"
# Zero
int_to_ip(`0`) -> \"0.0.0.0\"
# Max value
int_to_ip(`4294967295`) -> \"255.255.255.255\"
CLI Usage:
echo '{}' | jpx 'int_to_ip(`3232235777`)'
ip_to_int
Convert IP address to integer
Signature: string -> number
Examples:
# Private IP
ip_to_int('192.168.1.1') -> 3232235777
# Zero
ip_to_int('0.0.0.0') -> 0
# Max value
ip_to_int('255.255.255.255') -> 4294967295
CLI Usage:
echo '{}' | jpx 'ip_to_int(`"192.168.1.1"`)'
is_private_ip
Check if IP is in private range
Signature: string -> boolean
Examples:
# Class C private
is_private_ip('192.168.1.1') -> true
# Class A private
is_private_ip('10.0.0.1') -> true
# Public IP
is_private_ip('8.8.8.8') -> false
CLI Usage:
echo '{}' | jpx 'is_private_ip(`"192.168.1.1"`)'
Geolocation Functions
Geolocation functions: distance calculation, coordinate parsing, and geographic utilities.
Summary
| Function | Signature | Description |
|---|---|---|
geo_bearing | number, number, number, number -> number | Bearing between coordinates |
geo_distance | number, number, number, number -> number | Haversine distance in meters |
geo_distance_km | number, number, number, number -> number | Haversine distance in kilometers |
geo_distance_miles | number, number, number, number -> number | Haversine distance in miles |
Functions
geo_bearing
Bearing between coordinates
Signature: number, number, number, number -> number
Examples:
# NYC to London
geo_bearing(`40.7128`, `-74.0060`, `51.5074`, `-0.1278`) -> 51.2
# Due east
geo_bearing(`0`, `0`, `0`, `90`) -> 90.0
# Due north
geo_bearing(`0`, `0`, `90`, `0`) -> 0.0
CLI Usage:
echo '{}' | jpx 'geo_bearing(`40.7128`, `-74.0060`, `51.5074`, `-0.1278`)'
geo_distance
Haversine distance in meters
Signature: number, number, number, number -> number
Examples:
# NYC to London
geo_distance(`40.7128`, `-74.0060`, `51.5074`, `-0.1278`) -> 5570222
# LA to SF
geo_distance(`34.0522`, `-118.2437`, `37.7749`, `-122.4194`) -> 559044
# Same point
geo_distance(`0`, `0`, `0`, `0`) -> 0
CLI Usage:
echo '{}' | jpx 'geo_distance(`40.7128`, `-74.0060`, `51.5074`, `-0.1278`)'
geo_distance_km
Haversine distance in kilometers
Signature: number, number, number, number -> number
Examples:
# NYC to London
geo_distance_km(`40.7128`, `-74.0060`, `51.5074`, `-0.1278`) -> 5570.2
# LA to SF
geo_distance_km(`34.0522`, `-118.2437`, `37.7749`, `-122.4194`) -> 559.0
# Paris to London
geo_distance_km(`48.8566`, `2.3522`, `51.5074`, `-0.1278`) -> 343.5
CLI Usage:
echo '{}' | jpx 'geo_distance_km(`40.7128`, `-74.0060`, `51.5074`, `-0.1278`)'
geo_distance_miles
Haversine distance in miles
Signature: number, number, number, number -> number
Examples:
# NYC to London
geo_distance_miles(`40.7128`, `-74.0060`, `51.5074`, `-0.1278`) -> 3461.0
# LA to SF
geo_distance_miles(`34.0522`, `-118.2437`, `37.7749`, `-122.4194`) -> 347.4
# Paris to London
geo_distance_miles(`48.8566`, `2.3522`, `51.5074`, `-0.1278`) -> 213.4
CLI Usage:
echo '{}' | jpx 'geo_distance_miles(`40.7128`, `-74.0060`, `51.5074`, `-0.1278`)'
Path Functions
File path manipulation functions.
Summary
| Function | Signature | Description |
|---|---|---|
path_basename | string -> string | Get filename from path |
path_dirname | string -> string | Get directory from path |
path_ext | string -> string | Get file extension |
path_join | string... -> string | Join path segments |
Functions
path_basename
Get filename from path
Signature: string -> string
Examples:
# Unix path
path_basename('/foo/bar.txt') -> \"bar.txt\"
# Directory path
path_basename('/foo/bar/') -> \"bar\"
# Just filename
path_basename('file.txt') -> \"file.txt\"
CLI Usage:
echo '{}' | jpx 'path_basename(`"/foo/bar.txt"`)'
path_dirname
Get directory from path
Signature: string -> string
Examples:
# Unix path
path_dirname('/foo/bar.txt') -> \"/foo\"
# Nested path
path_dirname('/foo/bar/baz') -> \"/foo/bar\"
# No directory
path_dirname('file.txt') -> \"\"
CLI Usage:
echo '{}' | jpx 'path_dirname(`"/foo/bar.txt"`)'
path_ext
Get file extension
Signature: string -> string
Examples:
# Text file
path_ext('/foo/bar.txt') -> \"txt\"
# Image file
path_ext('image.png') -> \"png\"
# No extension
path_ext('noext') -> \"\"
CLI Usage:
echo '{}' | jpx 'path_ext(`"/foo/bar.txt"`)'
path_join
Join path segments
Signature: string... -> string
Examples:
# Multiple segments
path_join('/foo', 'bar', 'baz') -> \"/foo/bar/baz\"
# Full path
path_join('/home', 'user', 'file.txt') -> \"/home/user/file.txt\"
# Relative path
path_join('a', 'b') -> \"a/b\"
CLI Usage:
echo '{}' | jpx 'path_join(`"/foo"`, `"bar"`, `"baz"`)'
Format Functions
Data formatting functions for numbers, currencies, and other values.
Summary
| Function | Signature | Description |
|---|---|---|
from_csv | string -> array | Parse CSV string into array of arrays (jq parity) |
from_tsv | string -> array | Parse TSV string into array of arrays (jq parity) |
to_csv | array -> string | Convert array to CSV row string (RFC 4180 compliant) |
to_csv_rows | array -> string | Convert array of arrays to multi-line CSV string |
to_csv_table | array, array? -> string | Convert array of objects to CSV with header row |
to_tsv | array -> string | Convert array to TSV row string (tab-separated) |
Functions
from_csv
Parse CSV string into array of arrays (jq parity)
Signature: string -> array
Examples:
# Basic CSV parsing
from_csv("a,b,c\\n1,2,3") -> [["a", "b", "c"], ["1", "2", "3"]]
# Quoted fields with commas
from_csv("\"hello, world\",test") -> [["hello, world", "test"]]
# Empty string returns empty array
from_csv("") -> []
CLI Usage:
echo '{}' | jpx 'from_csv("a,b,c\\n1,2,3")'
from_tsv
Parse TSV string into array of arrays (jq parity)
Signature: string -> array
Examples:
# Basic TSV parsing
from_tsv("a\\tb\\tc\\n1\\t2\\t3") -> [["a", "b", "c"], ["1", "2", "3"]]
# Spaces preserved in fields
from_tsv("hello world\\ttest") -> [["hello world", "test"]]
# Empty string returns empty array
from_tsv("") -> []
CLI Usage:
echo '{}' | jpx 'from_tsv("a\\tb\\tc\\n1\\t2\\t3")'
to_csv
Convert array to CSV row string (RFC 4180 compliant)
Signature: array -> string
Examples:
# Simple strings
to_csv(['a', 'b', 'c']) -> "a,b,c"
# Mixed types
to_csv(['hello', `42`, `true`, `null`]) -> "hello,42,true,"
# Value with comma is quoted
to_csv(['hello, world', 'test']) -> "\"hello, world\",test"
# Quotes are doubled
to_csv(['say "hello"', 'test']) -> "\"say \"\"hello\"\"\",test"
CLI Usage:
echo '{}' | jpx 'to_csv(['a', `"b"`, 'c'])'
to_csv_rows
Convert array of arrays to multi-line CSV string
Signature: array -> string
Examples:
# Numeric rows
to_csv_rows([[`1`, `2`, `3`], [`4`, `5`, `6`]]) -> "1,2,3\n4,5,6"
# String rows
to_csv_rows([['a', 'b'], ['c', 'd']]) -> "a,b\nc,d"
# Empty array
to_csv_rows([]) -> ""
CLI Usage:
echo '{}' | jpx 'to_csv_rows([[`1`, `2`, `3`], [`4`, `5`, `6`]])'
to_csv_table
Convert array of objects to CSV with header row
Signature: array, array? -> string
Examples:
# Keys sorted alphabetically
to_csv_table([{name: 'alice', age: `30`}]) -> "age,name\n30,alice"
# Explicit column order
to_csv_table([{name: 'alice', age: `30`}], ['name', 'age']) -> "name,age\nalice,30"
# Missing fields become empty
to_csv_table([{a: `1`}, {a: `2`, b: `3`}], ['a', 'b']) -> "a,b\n1,\n2,3"
# Empty array
to_csv_table([]) -> ""
CLI Usage:
echo '{}' | jpx 'to_csv_table([{name: 'alice', age: `30`}])'
to_tsv
Convert array to TSV row string (tab-separated)
Signature: array -> string
Examples:
# Simple strings
to_tsv(['a', 'b', 'c']) -> "a\tb\tc"
# Mixed types
to_tsv(['hello', `42`, `true`]) -> "hello\t42\ttrue"
CLI Usage:
echo '{}' | jpx 'to_tsv(['a', `"b"`, 'c'])'
Color Functions
Color manipulation and conversion functions.
Summary
| Function | Signature | Description |
|---|---|---|
color_complement | string -> string | Get complementary color |
color_grayscale | string -> string | Convert to grayscale |
color_invert | string -> string | Invert a color |
color_mix | string, string, number -> string | Mix two colors |
darken | string, number -> string | Darken a color by percentage |
hex_to_rgb | string -> object | Convert hex color to RGB |
lighten | string, number -> string | Lighten a color by percentage |
rgb_to_hex | number, number, number -> string | Convert RGB to hex color |
Functions
color_complement
Get complementary color
Signature: string -> string
Examples:
# Red to cyan
color_complement('#ff0000') -> \"#00ffff\"
# Green to magenta
color_complement('#00ff00') -> \"#ff00ff\"
# Blue to yellow
color_complement('#0000ff') -> \"#ffff00\"
CLI Usage:
echo '{}' | jpx 'color_complement(`"#ff0000"`)'
color_grayscale
Convert to grayscale
Signature: string -> string
Examples:
# Red to gray
color_grayscale('#ff0000') -> \"#4c4c4c\"
# Green to gray
color_grayscale('#00ff00') -> \"#969696\"
# White unchanged
color_grayscale('#ffffff') -> \"#ffffff\"
CLI Usage:
echo '{}' | jpx 'color_grayscale(`"#ff0000"`)'
color_invert
Invert a color
Signature: string -> string
Examples:
# Invert red
color_invert('#ff0000') -> \"#00ffff\"
# Black to white
color_invert('#000000') -> \"#ffffff\"
# White to black
color_invert('#ffffff') -> \"#000000\"
CLI Usage:
echo '{}' | jpx 'color_invert(`"#ff0000"`)'
color_mix
Mix two colors
Signature: string, string, number -> string
Examples:
# Red and blue to purple
color_mix('#ff0000', '#0000ff', `50`) -> \"#800080\"
# Red and green
color_mix('#ff0000', '#00ff00', `50`) -> \"#808000\"
# Black and white to gray
color_mix('#000000', '#ffffff', `50`) -> \"#808080\"
CLI Usage:
echo '{}' | jpx 'color_mix(`"#ff0000"`, `"#0000ff"`, `50`)'
darken
Darken a color by percentage
Signature: string, number -> string
Examples:
# Darken blue by 20%
darken('#3366cc', `20`) -> \"#2952a3\"
# Darken red by 50%
darken('#ff0000', `50`) -> \"#800000\"
# White to black
darken('#ffffff', `100`) -> \"#000000\"
CLI Usage:
echo '{}' | jpx 'darken(`"#3366cc"`, `20`)'
hex_to_rgb
Convert hex color to RGB
Signature: string -> object
Examples:
# Orange
hex_to_rgb('#ff5500') -> {b: 0, g: 85, r: 255}
# Black
hex_to_rgb('#000000') -> {b: 0, g: 0, r: 0}
# White
hex_to_rgb('#ffffff') -> {b: 255, g: 255, r: 255}
CLI Usage:
echo '{}' | jpx 'hex_to_rgb(`"#ff5500"`)'
lighten
Lighten a color by percentage
Signature: string, number -> string
Examples:
# Lighten blue by 20%
lighten('#3366cc', `20`) -> \"#5c85d6\"
# Lighten dark red
lighten('#800000', `50`) -> \"#ff0000\"
# Black to white
lighten('#000000', `100`) -> \"#ffffff\"
CLI Usage:
echo '{}' | jpx 'lighten(`"#3366cc"`, `20`)'
rgb_to_hex
Convert RGB to hex color
Signature: number, number, number -> string
Examples:
# Orange
rgb_to_hex(`255`, `85`, `0`) -> \"#ff5500\"
# Black
rgb_to_hex(`0`, `0`, `0`) -> \"#000000\"
# White
rgb_to_hex(`255`, `255`, `255`) -> \"#ffffff\"
CLI Usage:
echo '{}' | jpx 'rgb_to_hex(`255`, `85`, `0`)'
Computing Functions
Computing-related utility functions.
Summary
| Function | Signature | Description |
|---|---|---|
bit_and | number, number -> number | Bitwise AND |
bit_not | number -> number | Bitwise NOT |
bit_or | number, number -> number | Bitwise OR |
bit_shift_left | number, number -> number | Bitwise left shift |
bit_shift_right | number, number -> number | Bitwise right shift |
bit_xor | number, number -> number | Bitwise XOR |
format_bytes | number -> string | Format bytes (decimal) |
format_bytes_binary | number -> string | Format bytes (binary) |
parse_bytes | string -> number | Parse byte size string |
Functions
bit_and
Bitwise AND
Signature: number, number -> number
Examples:
# 1100 AND 1010 = 1000
bit_and(`12`, `10`) -> 8
# Mask lower 4 bits
bit_and(`255`, `15`) -> 15
# 0111 AND 0011 = 0011
bit_and(`7`, `3`) -> 3
CLI Usage:
echo '{}' | jpx 'bit_and(`12`, `10`)'
bit_not
Bitwise NOT
Signature: number -> number
Examples:
# Invert zero
bit_not(`0`) -> -1
# Invert all ones
bit_not(`-1`) -> 0
# Invert one
bit_not(`1`) -> -2
CLI Usage:
echo '{}' | jpx 'bit_not(`0`)'
bit_or
Bitwise OR
Signature: number, number -> number
Examples:
# 1100 OR 1010 = 1110
bit_or(`12`, `10`) -> 14
# 0001 OR 0010 = 0011
bit_or(`1`, `2`) -> 3
# Zero OR any = any
bit_or(`0`, `255`) -> 255
CLI Usage:
echo '{}' | jpx 'bit_or(`12`, `10`)'
bit_shift_left
Bitwise left shift
Signature: number, number -> number
Examples:
# Shift 1 left by 4
bit_shift_left(`1`, `4`) -> 16
# Shift by 0 unchanged
bit_shift_left(`1`, `0`) -> 1
# Multiply by 4
bit_shift_left(`5`, `2`) -> 20
CLI Usage:
echo '{}' | jpx 'bit_shift_left(`1`, `4`)'
bit_shift_right
Bitwise right shift
Signature: number, number -> number
Examples:
# Divide by 4
bit_shift_right(`16`, `2`) -> 4
# Shift right by 4
bit_shift_right(`255`, `4`) -> 15
# Shift by 0 unchanged
bit_shift_right(`8`, `0`) -> 8
CLI Usage:
echo '{}' | jpx 'bit_shift_right(`16`, `2`)'
bit_xor
Bitwise XOR
Signature: number, number -> number
Examples:
# 1100 XOR 1010 = 0110
bit_xor(`12`, `10`) -> 6
# Same values = 0
bit_xor(`255`, `255`) -> 0
# 0101 XOR 0011 = 0110
bit_xor(`5`, `3`) -> 6
CLI Usage:
echo '{}' | jpx 'bit_xor(`12`, `10`)'
format_bytes
Format bytes (decimal)
Signature: number -> string
Examples:
# Gigabytes
format_bytes(`1500000000`) -> \"1.50 GB\"
# Kilobytes
format_bytes(`1000`) -> \"1.00 KB\"
# Bytes
format_bytes(`500`) -> \"500 B\"
CLI Usage:
echo '{}' | jpx 'format_bytes(`1500000000`)'
format_bytes_binary
Format bytes (binary)
Signature: number -> string
Examples:
# Gibibytes
format_bytes_binary(`1073741824`) -> \"1.00 GiB\"
# Kibibytes
format_bytes_binary(`1024`) -> \"1.00 KiB\"
# Mebibytes
format_bytes_binary(`1048576`) -> \"1.00 MiB\"
CLI Usage:
echo '{}' | jpx 'format_bytes_binary(`1073741824`)'
parse_bytes
Parse byte size string
Signature: string -> number
Examples:
# Gigabytes
parse_bytes('1.5 GB') -> 1500000000
# Kilobytes
parse_bytes('1 KB') -> 1000
# Gibibytes
parse_bytes('1 GiB') -> 1073741824
CLI Usage:
echo '{}' | jpx 'parse_bytes(`"1.5 GB"`)'
Utility Functions
General utility functions that don’t fit other categories.
Summary
| Function | Signature | Description |
|---|---|---|
coalesce | any... -> any | Return first non-null value |
default | any, any -> any | Return default value if null |
env | -> object | Get all environment variables as an object |
get_env | string -> string | null | Get a single environment variable by name |
if | boolean, any, any -> any | Conditional expression |
json_decode | string -> any | Parse JSON string |
json_encode | any -> string | Serialize value to JSON string |
json_pointer | any, string -> any | Access value using JSON Pointer (RFC 6901) |
now | -> number | Current Unix timestamp in seconds |
now_ms | -> number | Current Unix timestamp in milliseconds |
pretty | any, number? -> string | Pretty-print value as formatted JSON string |
Functions
coalesce
Return first non-null value
Signature: any... -> any
Examples:
# Skip nulls
coalesce(`null`, `null`, 'value') -> \"value\"
# Field fallback
coalesce(field1, field2, 'default') -> first non-null
# First wins
coalesce('first', 'second') -> 'first'
# All null
coalesce(`null`, `null`) -> null
CLI Usage:
echo '{}' | jpx 'coalesce(`null`, `null`, `"value"`)'
default
Return default value if null
Signature: any, any -> any
Examples:
# Null uses default
default(`null`, 'fallback') -> \"fallback\"
# Non-null keeps value
default('value', 'fallback') -> 'value'
# Missing field default
default(missing_field, `0`) -> 0
# Empty string not null
default('', 'fallback') -> ''
CLI Usage:
echo '{}' | jpx 'default(`null`, `"fallback"`)'
env
Get all environment variables as an object
Signature: -> object
Examples:
# All env vars
env() -> {HOME: '/Users/...', PATH: '...', ...}
# Access specific var
env().HOME -> home directory
# List all var names
keys(env()) -> list of var names
CLI Usage:
echo '{}' | jpx 'env()'
get_env
Get a single environment variable by name
Signature: string -> string | null
Examples:
# Get home directory
get_env('HOME') -> \"/Users/josh\"
# Get PATH
get_env('PATH') -> system PATH
# Returns null if not set
get_env('MISSING') -> null
# With default value
default(get_env('PORT'), '8080') -> with fallback
CLI Usage:
echo '{}' | jpx 'get_env(`"HOME"`)'
if
Conditional expression
Signature: boolean, any, any -> any
Examples:
# True branch
if(`true`, 'yes', 'no') -> \"yes\"
# False branch
if(`false`, 'yes', 'no') -> \"no\"
# Conditional logic
if(age >= `18`, 'adult', 'minor') -> depends
# Safe access
if(is_empty(arr), 'none', first(arr)) -> value or none
CLI Usage:
echo '{}' | jpx 'if(`true`, `"yes"`, `"no"`)'
json_decode
Parse JSON string
Signature: string -> any
Examples:
# Parse object
json_decode('{\"a\": 1}') -> {a: 1}
# Parse array
json_decode('[1, 2, 3]') -> [1, 2, 3]
# Parse string
json_decode('\"hello\"') -> \"hello\"
# Parse field
json_decode(json_field) -> parsed
CLI Usage:
echo '{}' | jpx 'json_decode(`"{\"a\": 1}"`)'
json_encode
Serialize value to JSON string
Signature: any -> string
Examples:
# Encode object
json_encode({a: 1}) -> \"{\\\"a\\\":1}\"
# Encode array
json_encode([1, 2, 3]) -> \"[1,2,3]\"
# Encode string
json_encode('hello') -> \"\\\"hello\\\"\"
# Encode any value
json_encode(data) -> JSON string
CLI Usage:
echo '{}' | jpx 'json_encode({a: 1})'
json_pointer
Access value using JSON Pointer (RFC 6901)
Signature: any, string -> any
Examples:
# Nested access
json_pointer({foo: {bar: 1}}, '/foo/bar') -> 1
# Array access
json_pointer(data, '/0/name') -> first item name
# Top-level access
json_pointer({a: 1}, '/a') -> 1
# Missing path
json_pointer(data, '/missing') -> null
CLI Usage:
echo '{}' | jpx 'json_pointer({foo: {bar: 1}}, `"/foo/bar"`)'
now
Current Unix timestamp in seconds
Signature: -> number
Examples:
# Current timestamp
now() -> 1699900000
# Subtract seconds
now() - 3600 -> one hour ago
# Add one day
now() + 86400 -> tomorrow
# Convert to string
from_epoch(now()) -> current ISO
CLI Usage:
echo '{}' | jpx 'now()'
now_ms
Current Unix timestamp in milliseconds
Signature: -> number
Examples:
# Current time in ms
now_ms() -> 1699900000000
# Calculate duration
now_ms() - start_ms -> elapsed ms
# Convert to seconds
now_ms() / `1000` -> seconds
# Convert to string
from_epoch_ms(now_ms()) -> current ISO
CLI Usage:
echo '{}' | jpx 'now_ms()'
pretty
Pretty-print value as formatted JSON string
Signature: any, number? -> string
Examples:
# Default 2-space indent
pretty({a: 1}) -> \"{\n \\\"a\\\": 1\n}\"
# Custom indent
pretty({a: 1}, `4`) -> 4-space indent
# Format for display
pretty(config) -> readable JSON
# Embed in output
sprintf('Config:\n%s', pretty(@)) -> embedded
CLI Usage:
echo '{}' | jpx 'pretty({a: 1})'
jmespath-extensions
The core Rust library providing 400+ extended JMESPath functions.
Installation
Add to your Cargo.toml:
[dependencies]
jmespath-extensions = "0.8"
Quick Start
use jmespath_extensions::search;
use serde_json::json;
fn main() {
let data = json!({"name": "hello world"});
let result = search("upper(name)", &data).unwrap();
assert_eq!(result, json!("HELLO WORLD"));
}
Features
The library uses feature flags to control which function categories are included:
full- All functions (default)core- String, array, object, math, type, utility (no external deps)- Individual categories:
string,array,hash,datetime,regex, etc.
# Minimal build with just core functions
jmespath-extensions = { version = "0.8", default-features = false, features = ["core"] }
# Just what you need
jmespath-extensions = { version = "0.8", default-features = false, features = ["string", "datetime"] }
API Documentation
Full API documentation is available on docs.rs:
jmespath-extensions on docs.rs
The rustdoc includes:
- All public functions with examples
- Function signatures and parameter types
- Category organization
- Feature flag requirements for each function
jpx-engine
Protocol-agnostic engine for JMESPath evaluation, function discovery, and JSON utilities. This is the core logic used by the MCP server and can be embedded in other applications.
Installation
Add to your Cargo.toml:
[dependencies]
jpx-engine = "0.1"
Quick Start
use jpx_engine::JpxEngine;
use serde_json::json;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let engine = JpxEngine::new();
// Evaluate expressions with extended functions
let data = json!({"items": [1, 2, 3, 4, 5]});
let result = engine.evaluate("sum(items)", &data)?;
println!("{}", result); // 15
// Discover available functions
let functions = engine.functions(Some("String"))?;
println!("String functions: {}", functions.len());
// Search functions by keyword
let results = engine.search_functions("date")?;
for r in results {
println!("{}: {}", r.name, r.description);
}
Ok(())
}
Features
Evaluation
evaluate()- Evaluate JMESPath expression against JSONevaluate_str()- Evaluate with JSON string inputbatch_evaluate()- Multiple expressions against same inputvalidate()- Check expression syntax without executing
Function Discovery
functions()- List all functions, optionally by categorycategories()- List all function categoriesdescribe_function()- Get detailed function infosearch_functions()- Fuzzy search by keywordsimilar_functions()- Find related functions
JSON Utilities
format()- Pretty-print JSONdiff()- Generate JSON Patch (RFC 6902)patch()- Apply JSON Patchmerge()- Apply JSON Merge Patch (RFC 7396)stats()- Analyze JSON structurepaths()- Extract all paths in dot notationkeys()- Extract object keys
Query Store
define_query()- Store named queriesrun_query()- Execute stored querieslist_queries()- List all stored queries
Strict Mode
#![allow(unused)]
fn main() {
let engine = JpxEngine::strict();
// Only standard JMESPath functions available
}
API Documentation
Full API documentation is available on docs.rs:
Thread Safety
JpxEngine is Send + Sync and can be safely shared across threads. The query store uses interior mutability with RwLock for concurrent access.
Installation
From PyPI
pip install jmespath-extensions
From Source
# Clone the repository
git clone https://github.com/joshrotenberg/jmespath-extensions
cd jmespath-extensions/crates/jmespath-extensions-py
# Install maturin if needed
pip install maturin
# Build and install
maturin develop --release
Verify Installation
import jmespath_extensions
# Check it works
result = jmespath_extensions.search("length(@)", [1, 2, 3])
print(result) # 3
# List available functions
functions = jmespath_extensions.list_functions()
print(f"Available functions: {len(functions)}")
Requirements
- Python 3.8+
- No runtime dependencies (Rust extensions are statically linked)
Usage
Basic Searching
The primary function is search(), which evaluates a JMESPath expression against data:
import jmespath_extensions as jpx
data = {
"users": [
{"name": "alice", "age": 30, "active": True},
{"name": "bob", "age": 25, "active": False},
{"name": "carol", "age": 35, "active": True}
]
}
# Extract active users
active = jpx.search("users[?active].name", data)
print(active) # ['alice', 'carol']
# Use extended functions
result = jpx.search("users | sort_by(@, &age) | [*].name", data)
print(result) # ['bob', 'alice', 'carol']
Compiled Expressions
For repeated use, compile the expression once:
import jmespath_extensions as jpx
# Compile once
expr = jpx.compile_expr("users[?age > `30`].name")
# Use many times
for dataset in datasets:
result = expr.search(dataset)
print(result)
Exploring Functions
List All Functions
import jmespath_extensions as jpx
# All functions
all_funcs = jpx.list_functions()
print(f"Total: {len(all_funcs)} functions")
# Filter by category
string_funcs = jpx.list_functions("string")
print(f"String functions: {string_funcs}")
List Categories
categories = jpx.list_categories()
print(categories)
# ['standard', 'string', 'array', 'object', 'math', 'datetime', ...]
Get Function Details
info = jpx.describe("split")
print(info)
# {
# 'name': 'split',
# 'category': 'String',
# 'signature': 'string, string -> array',
# 'description': 'Split string by delimiter...',
# 'example': 'split(`"a,b,c"`, `","`) -> ["a", "b", "c"]'
# }
Query Libraries
Parse and use .jpx query library files:
import jmespath_extensions as jpx
# Parse a query library
content = '''
-- :name active-users
-- :desc Get active users
users[?active]
-- :name user-count
length(users)
'''
library = jpx.parse_query_library(content)
# List queries
for query in library.list():
print(f"{query.name}: {query.description}")
# Get a specific query
query = library.get("active-users")
if query:
result = jpx.search(query.expression, data)
# Get all query names
names = library.names()
Check if Content is a Query Library
# Detect format
if jpx.is_query_library(content):
library = jpx.parse_query_library(content)
else:
# Plain expression
result = jpx.search(content.strip(), data)
Extended Functions Examples
String Functions
# Split and join
jpx.search('split(`"a,b,c"`, `","`)', {}) # ['a', 'b', 'c']
jpx.search('join(`"-"`, `["a","b","c"]`)', {}) # 'a-b-c'
# Case conversion
jpx.search('upper(`"hello"`)', {}) # 'HELLO'
jpx.search('snake_case(`"helloWorld"`)', {}) # 'hello_world'
Date/Time Functions
# Current time
jpx.search('now()', {}) # 1699900000 (Unix timestamp)
jpx.search('now_iso()', {}) # '2024-01-15T10:30:00Z'
# Parse and format dates
jpx.search('parse_datetime(`"2024-01-15"`, `"%Y-%m-%d"`)', {})
Math Functions
data = {"values": [1, 2, 3, 4, 5]}
jpx.search('mean(values)', data) # 3.0
jpx.search('std_dev(values)', data) # 1.414...
jpx.search('percentile(values, `50`)', data) # 3
Fuzzy Matching
tools = [
{"name": "create_user", "description": "Create a new user"},
{"name": "delete_user", "description": "Delete an existing user"},
{"name": "list_users", "description": "List all users"}
]
# Fuzzy search
results = jpx.search(
'fuzzy_search(@, `"name,description"`, `"user create"`)',
tools
)
Error Handling
import jmespath_extensions as jpx
try:
# Invalid expression
jpx.search("invalid[", {})
except ValueError as e:
print(f"Parse error: {e}")
try:
# Runtime error
jpx.search("length(@)", "not an array")
except Exception as e:
print(f"Evaluation error: {e}")
API Reference
Functions
search
def search(expression: str, data: Any) -> Any
Evaluate a JMESPath expression against data.
Parameters:
expression: JMESPath expression stringdata: Python data (dict, list, str, int, float, bool, None)
Returns: The result of the expression evaluation
Raises: ValueError for parse errors, Exception for runtime errors
Example:
result = jpx.search("users[0].name", {"users": [{"name": "alice"}]})
# Returns: "alice"
compile_expr
def compile_expr(expression: str) -> CompiledExpression
Compile a JMESPath expression for repeated use.
Parameters:
expression: JMESPath expression string
Returns: CompiledExpression object
Raises: ValueError for parse errors
Example:
expr = jpx.compile_expr("length(@)")
result = expr.search([1, 2, 3]) # 3
list_functions
def list_functions(category: Optional[str] = None) -> List[str]
List available function names.
Parameters:
category: Optional category filter (e.g., “string”, “math”, “datetime”)
Returns: List of function names
Example:
all_funcs = jpx.list_functions()
string_funcs = jpx.list_functions("string")
list_categories
def list_categories() -> List[str]
List all function categories.
Returns: List of category names
Example:
categories = jpx.list_categories()
# ['standard', 'string', 'array', 'object', 'math', ...]
describe
def describe(name: str) -> Optional[Dict]
Get detailed information about a function.
Parameters:
name: Function name or alias
Returns: Dictionary with function info, or None if not found
Return fields:
name: Function namecategory: Category namesignature: Type signaturedescription: Description textexample: Example usage
Example:
info = jpx.describe("split")
print(info["signature"]) # "string, string -> array"
parse_query_library
def parse_query_library(content: str) -> QueryLibrary
Parse a query library from string content.
Parameters:
content: Query library content in.jpxformat
Returns: QueryLibrary object
Raises: ValueError for parse errors
Example:
library = jpx.parse_query_library('''
-- :name my-query
-- :desc A sample query
users[?active]
''')
is_query_library
def is_query_library(content: str) -> bool
Check if content appears to be a query library format.
Parameters:
content: String content to check
Returns: True if content starts with query library directives
Example:
if jpx.is_query_library(content):
library = jpx.parse_query_library(content)
Classes
CompiledExpression
A compiled JMESPath expression for efficient repeated evaluation.
Methods
search
def search(self, data: Any) -> Any
Evaluate this expression against data.
Parameters:
data: Python data to evaluate against
Returns: Evaluation result
QueryLibrary
A collection of named queries parsed from a .jpx file.
Methods
get
def get(self, name: str) -> Optional[NamedQuery]
Get a query by name.
Parameters:
name: Query name
Returns: NamedQuery or None
list
def list(self) -> List[NamedQuery]
Get all queries in the library.
Returns: List of NamedQuery objects
names
def names(self) -> List[str]
Get all query names.
Returns: List of query names
NamedQuery
A single named query from a query library.
Properties
name: str- Query namedescription: Optional[str]- Query descriptionexpression: str- JMESPath expressionline_number: int- Line number in source file
Example:
query = library.get("my-query")
if query:
print(f"{query.name}: {query.description}")
result = jpx.search(query.expression, data)
Real-World Datasets
Learn jpx by working with real data from public APIs. Each example includes:
- How to fetch the data
- Data structure overview
- Progressive examples from basic to advanced
- Practical use cases
Available Guides
| Guide | Description | Key Features |
|---|---|---|
| Standard JMESPath Only | Portable queries using only spec functions | 26 built-in functions, no extensions |
| NLP Text Processing | Text analysis pipelines | Tokenization, stemming, stopwords, normalization |
| Hacker News | Tech discussions via Algolia API | NLP on real content, topic detection, vocabulary analysis |
| USGS Earthquakes | Real-time seismic data | Geo functions, statistics, filtering |
| Nobel Prize API | Laureates and prizes | Multilingual data, text processing, dates |
| NASA Near Earth Objects | Asteroids and comets | Nested data, unit conversions, risk analysis |
| Project Management | Synthetic project data | Comprehensive function coverage, all categories |
Quick Start
# Fetch earthquake data
curl -s "https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&limit=20&minmagnitude=5" > quakes.json
# Try a query
jpx 'features[*].{mag: properties.mag, place: properties.place}' quakes.json
What You’ll Learn
Filtering & Selection
- Complex filter expressions with multiple conditions
- Nested field access patterns
- Text-based filtering with
contains,starts_with
Statistics & Aggregation
avg,median,stddevfor numeric analysismin,max,min_by,max_byfor extremeslengthand counting patterns
Geographic Calculations
geo_distance_kmfor distance calculations- Coordinate extraction and formatting
- Distance-based sorting
Date/Time Operations
- Unix timestamp conversion with
from_unixtime - Date formatting with
format_datetime - Date range filtering
Data Transformation
- Reshaping nested structures
- Flattening for export
- CSV/TSV output for spreadsheets
Pipeline Patterns
- Multi-step transformations
- Sorting and limiting results
- Building summary reports
Tips for Working with APIs
-
Save data locally for faster iteration:
curl -s "API_URL" > data.json jpx 'expression' data.json -
Explore structure first:
jpx 'keys(@)' data.json # Top-level keys jpx '@[0]' data.json # First element (arrays) jpx 'type(@)' data.json # Data type -
Use
--compactfor pipelines:jpx -c 'expression' data.json | jpx 'next_expression' -
Export for analysis:
jpx --csv 'transform' data.json > output.csv
More Data Sources
Looking for more datasets to practice with? Check out:
- Awesome JSON Datasets - Curated list of public JSON APIs
- Public APIs - Collective list of free APIs
- NASA Open APIs - Space and Earth science data
- OpenWeatherMap - Weather data
- GitHub API - Repository and user data
Standard JMESPath Only
This guide demonstrates jpx using only standard JMESPath functions - no extensions. This is useful when:
- You need queries portable to other JMESPath implementations
- You’re learning JMESPath fundamentals
- You want to understand what’s possible without extensions
You can also use jpx --strict to enforce standard-only mode, which will error if you accidentally use an extension function.
Standard Functions Reference
JMESPath defines 26 built-in functions:
| Function | Description |
|---|---|
abs(n) | Absolute value |
avg(arr) | Average of numbers |
ceil(n) | Round up |
contains(subject, search) | Check if contains value |
ends_with(str, suffix) | Check string suffix |
floor(n) | Round down |
join(sep, arr) | Join array to string |
keys(obj) | Get object keys |
length(val) | Length of array/string/object |
map(&expr, arr) | Apply expression to each element |
max(arr) | Maximum value |
max_by(arr, &expr) | Element with max expression value |
merge(obj1, obj2, ...) | Merge objects |
min(arr) | Minimum value |
min_by(arr, &expr) | Element with min expression value |
not_null(val1, val2, ...) | First non-null value |
reverse(arr) | Reverse array or string |
sort(arr) | Sort array |
sort_by(arr, &expr) | Sort by expression |
starts_with(str, prefix) | Check string prefix |
sum(arr) | Sum of numbers |
to_array(val) | Convert to array |
to_number(val) | Convert to number |
to_string(val) | Convert to string |
type(val) | Get value type |
values(obj) | Get object values |
Sample Data
We’ll use this sample dataset throughout the guide:
cat > books.json << 'EOF'
{
"library": {
"name": "City Library",
"books": [
{"id": 1, "title": "The Great Gatsby", "author": "F. Scott Fitzgerald", "year": 1925, "genres": ["fiction", "classic"], "rating": 4.2, "available": true},
{"id": 2, "title": "To Kill a Mockingbird", "author": "Harper Lee", "year": 1960, "genres": ["fiction", "classic"], "rating": 4.5, "available": false},
{"id": 3, "title": "1984", "author": "George Orwell", "year": 1949, "genres": ["fiction", "dystopian"], "rating": 4.3, "available": true},
{"id": 4, "title": "Pride and Prejudice", "author": "Jane Austen", "year": 1813, "genres": ["fiction", "romance"], "rating": 4.4, "available": true},
{"id": 5, "title": "The Catcher in the Rye", "author": "J.D. Salinger", "year": 1951, "genres": ["fiction", "classic"], "rating": 3.9, "available": false},
{"id": 6, "title": "Brave New World", "author": "Aldous Huxley", "year": 1932, "genres": ["fiction", "dystopian"], "rating": 4.1, "available": true},
{"id": 7, "title": "The Hobbit", "author": "J.R.R. Tolkien", "year": 1937, "genres": ["fiction", "fantasy"], "rating": 4.6, "available": true},
{"id": 8, "title": "Animal Farm", "author": "George Orwell", "year": 1945, "genres": ["fiction", "satire"], "rating": 4.0, "available": false}
]
}
}
EOF
Basic Selection
Access Nested Data
jpx --strict 'library.name' books.json
Output: "City Library"
Get All Books
jpx --strict 'library.books' books.json
Select Specific Fields
jpx --strict 'library.books[*].title' books.json
Output:
["The Great Gatsby", "To Kill a Mockingbird", "1984", "Pride and Prejudice", "The Catcher in the Rye", "Brave New World", "The Hobbit", "Animal Farm"]
Project Multiple Fields
jpx --strict 'library.books[*].{title: title, author: author}' books.json
Output:
[
{"title": "The Great Gatsby", "author": "F. Scott Fitzgerald"},
{"title": "To Kill a Mockingbird", "author": "Harper Lee"},
...
]
Filtering
Filter by Condition
jpx --strict 'library.books[?available == `true`].title' books.json
Output:
["The Great Gatsby", "1984", "Pride and Prejudice", "Brave New World", "The Hobbit"]
Filter by Numeric Comparison
jpx --strict 'library.books[?rating >= `4.3`].{title: title, rating: rating}' books.json
Output:
[
{"title": "To Kill a Mockingbird", "rating": 4.5},
{"title": "1984", "rating": 4.3},
{"title": "Pride and Prejudice", "rating": 4.4},
{"title": "The Hobbit", "rating": 4.6}
]
Filter by Year Range
jpx --strict 'library.books[?year >= `1940` && year < `1960`].title' books.json
Output:
["1984", "The Catcher in the Rye", "Animal Farm"]
Filter with String Functions
# Books by authors starting with "J"
jpx --strict 'library.books[?starts_with(author, `J`)].{title: title, author: author}' books.json
Output:
[
{"title": "Pride and Prejudice", "author": "Jane Austen"},
{"title": "The Catcher in the Rye", "author": "J.D. Salinger"},
{"title": "The Hobbit", "author": "J.R.R. Tolkien"}
]
Filter with Contains
# Books with "The" in the title
jpx --strict 'library.books[?contains(title, `The`)].title' books.json
Output:
["The Great Gatsby", "The Catcher in the Rye", "The Hobbit"]
Check Array Membership
# Books in the "dystopian" genre
jpx --strict 'library.books[?contains(genres, `dystopian`)].title' books.json
Output:
["1984", "Brave New World"]
Sorting
Sort by Field
jpx --strict 'sort_by(library.books, &year)[*].{title: title, year: year}' books.json
Output (oldest first):
[
{"title": "Pride and Prejudice", "year": 1813},
{"title": "The Great Gatsby", "year": 1925},
{"title": "Brave New World", "year": 1932},
...
]
Sort Descending (Reverse)
jpx --strict 'reverse(sort_by(library.books, &rating))[*].{title: title, rating: rating}' books.json
Output (highest rated first):
[
{"title": "The Hobbit", "rating": 4.6},
{"title": "To Kill a Mockingbird", "rating": 4.5},
{"title": "Pride and Prejudice", "rating": 4.4},
...
]
Sort Simple Arrays
jpx --strict 'sort(library.books[*].year)' books.json
Output:
[1813, 1925, 1932, 1937, 1945, 1949, 1951, 1960]
Aggregation
Count Items
jpx --strict 'length(library.books)' books.json
Output: 8
Count Filtered Items
jpx --strict 'length(library.books[?available == `true`])' books.json
Output: 5
Sum Values
jpx --strict 'sum(library.books[*].year)' books.json
Output: 15512
Average
jpx --strict 'avg(library.books[*].rating)' books.json
Output: 4.25
Min and Max
jpx --strict '{
oldest_year: min(library.books[*].year),
newest_year: max(library.books[*].year),
lowest_rating: min(library.books[*].rating),
highest_rating: max(library.books[*].rating)
}' books.json
Output:
{
"oldest_year": 1813,
"newest_year": 1960,
"lowest_rating": 3.9,
"highest_rating": 4.6
}
Find Element with Min/Max
# Highest rated book
jpx --strict 'max_by(library.books, &rating).{title: title, rating: rating}' books.json
Output:
{"title": "The Hobbit", "rating": 4.6}
# Oldest book
jpx --strict 'min_by(library.books, &year).{title: title, year: year}' books.json
Output:
{"title": "Pride and Prejudice", "year": 1813}
Object Operations
Get Keys
jpx --strict 'keys(library.books[0])' books.json
Output:
["id", "title", "author", "year", "genres", "rating", "available"]
Get Values
jpx --strict 'values(library.books[0])' books.json
Output:
[1, "The Great Gatsby", "F. Scott Fitzgerald", 1925, ["fiction", "classic"], 4.2, true]
Merge Objects
jpx --strict 'merge(library.books[0], {checked_out: `true`, due_date: `2025-02-01`})' books.json
Output:
{
"id": 1,
"title": "The Great Gatsby",
"author": "F. Scott Fitzgerald",
"year": 1925,
"genres": ["fiction", "classic"],
"rating": 4.2,
"available": true,
"checked_out": true,
"due_date": "2025-02-01"
}
Type Conversions
Check Type
jpx --strict 'type(library.books)' books.json
Output: "array"
jpx --strict 'type(library.books[0].rating)' books.json
Output: "number"
Convert to String
jpx --strict 'library.books[*].{title: title, year_str: to_string(year)}' books.json
Convert to Number
echo '{"value": "42"}' | jpx --strict 'to_number(value)'
Output: 42
Convert to Array
jpx --strict 'to_array(library.name)' books.json
Output: ["City Library"]
String Operations
Join Array Elements
jpx --strict 'join(`, `, library.books[*].title)' books.json
Output: "The Great Gatsby, To Kill a Mockingbird, 1984, Pride and Prejudice, The Catcher in the Rye, Brave New World, The Hobbit, Animal Farm"
Check Prefix/Suffix
# Titles ending with specific words
jpx --strict 'library.books[?ends_with(title, `Rye`)].title' books.json
Output: ["The Catcher in the Rye"]
The Map Function
The map function applies an expression to each element:
# Extract just the year from each book
jpx --strict 'map(&year, library.books)' books.json
Output:
[1925, 1960, 1949, 1813, 1951, 1932, 1937, 1945]
# Create formatted strings
jpx --strict 'map(&{book: title, published: year}, library.books)' books.json
Handling Nulls
First Non-Null Value
echo '{"a": null, "b": null, "c": "found"}' | jpx --strict 'not_null(a, b, c)'
Output: "found"
Null-Safe Field Access
echo '{"items": [{"name": "a", "value": 1}, {"name": "b"}]}' | \
jpx --strict 'items[*].{name: name, value: not_null(value, `0`)}'
Output:
[
{"name": "a", "value": 1},
{"name": "b", "value": 0}
]
Pipelines
Chain operations with the pipe operator |:
# Filter, sort, and take top 3
jpx --strict 'library.books
| [?available == `true`]
| sort_by(@, &rating)
| reverse(@)
| [:3]
| [*].{title: title, rating: rating}' books.json
Output:
[
{"title": "The Hobbit", "rating": 4.6},
{"title": "Pride and Prejudice", "rating": 4.4},
{"title": "1984", "rating": 4.3}
]
Complex Example: Library Report
Combining multiple standard functions:
jpx --strict '{
library_name: library.name,
total_books: length(library.books),
available_count: length(library.books[?available == `true`]),
checked_out_count: length(library.books[?available == `false`]),
average_rating: avg(library.books[*].rating),
rating_range: {
min: min(library.books[*].rating),
max: max(library.books[*].rating)
},
year_range: {
oldest: min(library.books[*].year),
newest: max(library.books[*].year)
},
top_rated: max_by(library.books, &rating).title,
authors: sort(library.books[*].author),
available_titles: sort(library.books[?available == `true`][*].title)
}' books.json
Output:
{
"library_name": "City Library",
"total_books": 8,
"available_count": 5,
"checked_out_count": 3,
"average_rating": 4.25,
"rating_range": {"min": 3.9, "max": 4.6},
"year_range": {"oldest": 1813, "newest": 1960},
"top_rated": "The Hobbit",
"authors": ["Aldous Huxley", "F. Scott Fitzgerald", "George Orwell", "George Orwell", "Harper Lee", "J.D. Salinger", "J.R.R. Tolkien", "Jane Austen"],
"available_titles": ["1984", "Brave New World", "Pride and Prejudice", "The Great Gatsby", "The Hobbit"]
}
When to Use Extensions
Standard JMESPath is powerful for basic transformations, but you’ll want extensions for:
- String manipulation:
split,trim,lower,upper,replace - Date/time:
from_epoch,format_date,date_diff - Advanced math:
median,stddev,round,percentile - Grouping:
group_by,frequencies,unique - Text processing:
tokens,stems,word_count - Hashing:
sha256,md5,base64_encode - And 350+ more functions
To see what’s available:
jpx --list-functions
jpx --search "group"
jpx --describe group_by
Related
NLP Text Processing
jpx includes a comprehensive NLP toolkit for text analysis and processing. This guide demonstrates how to build text processing pipelines combining tokenization, stemming, stopword removal, and more.
NLP Pipeline Overview
A typical NLP preprocessing pipeline:
Raw Text → Tokenize → Remove Stopwords → Stem → Analyze
jpx makes this composable with pipes:
echo '"The quick brown foxes are running quickly"' | \
jpx 'tokens(@) | remove_stopwords(@) | stems(@)'
# ["quick", "brown", "fox", "run", "quick"]
Tokenization
Basic Tokenization
The tokens function provides simple, normalized tokenization:
echo '"Hello, World! This is a TEST."' | jpx 'tokens(@)'
# ["hello", "world", "this", "is", "a", "test"]
Configurable Tokenization
Use tokenize for more control:
# Preserve case
echo '"Hello World"' | jpx 'tokenize(@, `{"case": "preserve"}`)'
# ["Hello", "World"]
# Keep punctuation
echo '"Hello, World!"' | jpx 'tokenize(@, `{"punctuation": "keep"}`)'
# ["hello,", "world!"]
# Both options
echo '"Hello, World!"' | jpx 'tokenize(@, `{"case": "preserve", "punctuation": "keep"}`)'
# ["Hello,", "World!"]
Stop Word Removal
Stop words are common words (the, is, at, which) that often don’t add meaning for analysis.
Check Stopwords
# Get English stopwords
echo '{}' | jpx 'stopwords() | length(@)'
# 179
# Check specific words
echo '{}' | jpx 'is_stopword(`"the"`)'
# true
echo '{}' | jpx 'is_stopword(`"algorithm"`)'
# false
Filter Stopwords from Tokens
echo '["the", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog"]' | \
jpx 'remove_stopwords(@)'
# ["quick", "brown", "fox", "jumps", "lazy", "dog"]
Multilingual Support
Stopwords are available in 30+ languages:
# Spanish
echo '{}' | jpx 'stopwords(`"es"`) | slice(@, `0`, `5`)'
# ["de", "la", "que", "el", "en"]
# German
echo '{}' | jpx 'stopwords(`"de"`) | slice(@, `0`, `5`)'
# ["aber", "alle", "allem", "allen", "aller"]
# French
echo '["le", "chat", "noir", "est", "rapide"]' | jpx 'remove_stopwords(@, `"fr"`)'
# ["chat", "noir", "rapide"]
Stemming
Stemming reduces words to their root form, helping match variations like “running”, “runs”, “ran” to “run”.
Single Word Stemming
echo '{}' | jpx 'stem(`"running"`)'
# "run"
echo '{}' | jpx 'stem(`"connections"`)'
# "connect"
echo '{}' | jpx 'stem(`"happiness"`)'
# "happi"
Stem Token Arrays
echo '["running", "jumps", "walked", "swimming"]' | jpx 'stems(@)'
# ["run", "jump", "walk", "swim"]
Multilingual Stemming
Supports 17 languages via Snowball stemmers:
# Spanish
echo '{}' | jpx 'stem(`"corriendo"`, `"es"`)'
# "corr"
# German
echo '{}' | jpx 'stem(`"laufend"`, `"de"`)'
# "lauf"
# French
echo '["courant", "marchant", "parlant"]' | jpx 'stems(@, `"fr"`)'
# ["cour", "march", "parl"]
Supported languages: ar (Arabic), da (Danish), de (German), el (Greek), en (English), es (Spanish), fi (Finnish), fr (French), hu (Hungarian), it (Italian), nl (Dutch), no (Norwegian), pt (Portuguese), ro (Romanian), ru (Russian), sv (Swedish), tr (Turkish).
Text Normalization
Unicode Normalization
Normalize text to consistent Unicode forms:
# NFC (composed) - default
echo '"café"' | jpx 'normalize_unicode(@)'
# "café"
# NFKC - compatibility composed (expands ligatures)
echo '"file"' | jpx 'normalize_unicode(@, `"nfkc"`)'
# "file"
Remove Accents
Strip diacritical marks for accent-insensitive matching:
echo '"café résumé naïve"' | jpx 'remove_accents(@)'
# "cafe resume naive"
echo '"señor über"' | jpx 'remove_accents(@)'
# "senor uber"
Collapse Whitespace
Normalize irregular whitespace:
echo '"hello world\t\ntest"' | jpx 'collapse_whitespace(@)'
# "hello world test"
Complete NLP Pipelines
Basic Text Preprocessing
TEXT='"The quick brown foxes are running and jumping quickly over the lazy dogs."'
echo "$TEXT" | jpx '
tokens(@)
| remove_stopwords(@)
| stems(@)
'
# ["quick", "brown", "fox", "run", "jump", "quick", "lazi", "dog"]
Word Frequency Analysis
TEXT='"The cat sat on the mat. The cat was happy. The mat was soft."'
# Get word frequencies after preprocessing
echo "$TEXT" | jpx '
tokens(@)
| remove_stopwords(@)
| frequencies(@)
'
# {"cat": 2, "sat": 1, "mat": 2, "happi": 1, "soft": 1}
# With stemming first
echo "$TEXT" | jpx '
tokens(@)
| remove_stopwords(@)
| stems(@)
| frequencies(@)
'
N-gram Analysis After Preprocessing
TEXT='"Machine learning models require training data"'
# Word bigrams
echo "$TEXT" | jpx '
tokens(@)
| remove_stopwords(@)
| join(` `, @)
| bigrams(@)
'
# [["machine", "learning"], ["learning", "models"], ["models", "require"], ["require", "training"], ["training", "data"]]
Multilingual Pipeline
# French text processing
FRENCH='"Les chats noirs sont très rapides et intelligents"'
echo "$FRENCH" | jpx '
tokens(@)
| remove_stopwords(@, `"fr"`)
| stems(@, `"fr"`)
'
# ["chat", "noir", "rapid", "intelligent"]
Working with JSON Data
Process Text Fields in Objects
cat <<'EOF' | jpx '
[*].{
id: id,
original: text,
tokens: tokens(text) | remove_stopwords(@) | stems(@),
word_count: word_count(text)
}
'
[
{"id": 1, "text": "The quick brown fox jumps over the lazy dog"},
{"id": 2, "text": "Machine learning algorithms are transforming industries"},
{"id": 3, "text": "Natural language processing enables text analysis"}
]
EOF
Extract and Analyze Descriptions
# Analyze product descriptions
cat <<'EOF' | jpx '
products[*].{
name: name,
keywords: tokens(description) | remove_stopwords(@) | stems(@) | unique(@)
}
'
{
"products": [
{"name": "Widget A", "description": "A fantastic widget for organizing your daily tasks"},
{"name": "Widget B", "description": "An amazing widget for managing your weekly schedule"}
]
}
EOF
Text Similarity Preparation
Prepare text for similarity comparison:
cat <<'EOF' | jpx '
[*].{
id: id,
normalized: text
| lower(@)
| remove_accents(collapse_whitespace(@))
| tokens(@)
| remove_stopwords(@)
| stems(@)
| sort(@)
| join(` `, @)
}
'
[
{"id": 1, "text": "The café serves excellent coffee"},
{"id": 2, "text": "Cafés serving good coffees"}
]
EOF
# Both normalize to similar representations for comparison
Combining with Other jpx Functions
With Language Detection
cat <<'EOF' | jpx '
[*].{
text: text,
language: detect_language_iso(text),
tokens: tokens(text)
}
'
[
{"text": "Hello world, this is English"},
{"text": "Bonjour le monde, ceci est français"},
{"text": "Hola mundo, esto es español"}
]
EOF
With Fuzzy Matching
Find similar terms after stemming:
echo '{}' | jpx '{
terms: [`"running"`, `"runner"`, `"ran"`, `"run"`] | stems(@) | unique(@),
similar: jaro_winkler(`"run"`, `"ran"`)
}'
# {"terms": ["run"], "similar": 0.933...}
With Phonetic Matching
Combine text normalization with phonetic codes:
echo '["café", "cafe", "caffè"]' | jpx '[*].{
original: @,
normalized: remove_accents(@),
soundex: soundex(remove_accents(@))
}'
Performance Tips
-
Chain operations efficiently: The pipe operator streams data without intermediate allocations
-
Filter early: Remove stopwords before expensive operations like stemming
-
Use
tokensfor simple cases: It’s optimized for the common case (lowercase, no punctuation) -
Batch process: Process arrays of text rather than calling functions repeatedly
# Good: Process array at once
echo '["text1", "text2", "text3"]' | jpx '[*] | stems(tokens(@[0]))'
# Better: Use map expressions
echo '{"texts": ["running fast", "jumping high"]}' | jpx 'texts[*] | [*].tokens(@)'
Using Query Libraries
Save your NLP pipelines in a .jpx query library for reuse. See examples/nlp.jpx for ready-to-use text processing queries:
# List available NLP queries
jpx -Q examples/nlp.jpx --list-queries
# Clean HTML from text
echo '"<p>Hello <b>World</b>!</p>"' | jpx -Q examples/nlp.jpx:clean-html
# Extract keywords
echo '"The quick brown foxes are running quickly"' | jpx -Q examples/nlp.jpx:extract-keywords
# Get reading statistics
cat article.txt | jpx -Q examples/nlp.jpx:reading-stats
Create your own domain-specific library:
-- :name preprocess
-- :desc Standard preprocessing pipeline
tokens(@) | remove_stopwords(@) | stems(@)
-- :name keyword-extract
-- :desc Top 10 keywords from text
tokens(@) | remove_stopwords(@) | stems(@) | frequencies(@) | items(@) | sort_by(@, &[1]) | reverse(@) | [:10][*][0]
See Query Files for more on creating and using query libraries.
Related Functions
- Language Detection -
detect_language,detect_script - Fuzzy Matching -
levenshtein,jaro_winkler - Phonetic -
soundex,metaphone,sounds_like - String Functions -
lower,upper,trim,split
Hacker News
The Hacker News Algolia API provides searchable access to HN stories, comments, and discussions. This dataset is excellent for NLP analysis because it has:
- Rich technical text (story titles, Ask HN posts, comments)
- Metadata for filtering (points, comments, timestamps)
- Real developer discussions with domain-specific vocabulary
- Multilingual content (occasional non-English posts)
Getting the Data
# Front page stories
curl -s "https://hn.algolia.com/api/v1/search?tags=front_page&hitsPerPage=50" > hn_front.json
# Ask HN posts (rich text content)
curl -s "https://hn.algolia.com/api/v1/search?tags=ask_hn&hitsPerPage=50" > hn_ask.json
# Show HN posts
curl -s "https://hn.algolia.com/api/v1/search?tags=show_hn&hitsPerPage=50" > hn_show.json
# Search for specific topics
curl -s "https://hn.algolia.com/api/v1/search?query=rust&tags=story&hitsPerPage=50" > hn_rust.json
# Comments on a story
curl -s "https://hn.algolia.com/api/v1/search?tags=comment,story_12345&hitsPerPage=100" > comments.json
Data Structure
The search API returns:
{
"hits": [
{
"objectID": "37392676",
"title": "Ask HN: I'm an FCC Commissioner proposing regulation of IoT security",
"url": null,
"author": "SimingtonFCC",
"points": 2847,
"story_text": "Hi everyone, I'm FCC Commissioner Nathan Simington...",
"num_comments": 475,
"created_at": "2023-09-05T15:00:00.000Z",
"created_at_i": 1693926000,
"_tags": ["story", "author_SimingtonFCC", "story_37392676", "ask_hn"]
}
],
"nbHits": 12345,
"page": 0,
"nbPages": 50,
"hitsPerPage": 50
}
Key fields:
title- Story headline (always present)story_text- Full text for Ask HN / Show HN posts (HTML)url- External link (null for Ask HN)points- Upvotesnum_comments- Discussion size_tags- Includesask_hn,show_hn,front_page, etc.
Basic Queries
List Story Titles
jpx 'hits[*].title' hn_front.json
Get Top Stories with Metadata
jpx 'hits[*].{
title: title,
points: points,
comments: num_comments,
author: author
}' hn_front.json
Filter by Points
jpx 'hits[?points > `500`].title' hn_front.json
Sort by Discussion Size
jpx 'sort_by(hits, &num_comments) | reverse(@) | [:10].{
title: title,
comments: num_comments
}' hn_front.json
NLP Analysis on Titles
Tokenize and Analyze Titles
# Get word frequencies across all titles
jpx 'hits[*].title | join(` `, @) | tokens(@) | remove_stopwords(@) | frequencies(@)' hn_front.json
Extract Keywords from Top Stories
jpx 'hits[?points > `200`].title
| join(` `, @)
| tokens(@)
| remove_stopwords(@)
| stems(@)
| frequencies(@)' hn_front.json
Find Common Technical Terms
# Stem and count to normalize variations (Rust/rust, APIs/API)
jpx 'hits[*].title
| join(` `, @)
| tokens(@)
| stems(@)
| frequencies(@)
| items(@)
| sort_by(@, &[1])
| reverse(@)
| [:20]' hn_front.json
Bigram Analysis on Headlines
jpx 'hits[*].title | join(` `, @) | bigrams(@) | frequencies(@)' hn_front.json
Analyzing Ask HN Posts
Ask HN posts have rich story_text content - perfect for NLP.
Extract and Clean Story Text
# Remove HTML tags and normalize
jpx 'hits[0].story_text
| regex_replace(@, `<[^>]+>`, ` `)
| collapse_whitespace(@)' hn_ask.json
Full Text Analysis Pipeline
jpx 'hits[0].story_text
| regex_replace(@, `<[^>]+>`, ` `)
| tokens(@)
| remove_stopwords(@)
| stems(@)
| frequencies(@)
| items(@)
| sort_by(@, &[1])
| reverse(@)
| [:15]' hn_ask.json
Compare Vocabulary Across Posts
# Extract top keywords per Ask HN post
jpx 'hits[:5] | [*].{
title: title,
keywords: story_text
| regex_replace(@, `<[^>]+>`, ` `)
| tokens(@)
| remove_stopwords(@)
| stems(@)
| frequencies(@)
| items(@)
| sort_by(@, &[1])
| reverse(@)
| [:5][*][0]
}' hn_ask.json
Reading Time Estimates
jpx 'hits[:10] | [*].{
title: title,
reading_time: story_text | regex_replace(@, `<[^>]+>`, ` `) | reading_time(@),
word_count: story_text | regex_replace(@, `<[^>]+>`, ` `) | word_count(@)
}' hn_ask.json
Topic Detection
Categorize by Keywords
# Find AI/ML related posts
jpx 'hits[?contains(lower(title), `ai`) ||
contains(lower(title), `llm`) ||
contains(lower(title), `gpt`) ||
contains(lower(title), `machine learning`)].{
title: title,
points: points
}' hn_front.json
Programming Language Mentions
jpx 'hits[*].{
title: title,
mentions_rust: contains(lower(title), `rust`),
mentions_go: regex_match(lower(title), `\\bgo\\b`),
mentions_python: contains(lower(title), `python`)
} | [?mentions_rust || mentions_go || mentions_python]' hn_front.json
N-gram Topic Extraction
# Find common 2-word phrases (potential topics)
jpx 'hits[*].title
| [*] | join(` `, @)
| lower(@)
| ngrams(@, `2`, `word`)
| frequencies(@)
| items(@)
| sort_by(@, &[1])
| reverse(@)
| [:10]' hn_front.json
Sentiment and Engagement Analysis
High Engagement Posts
# Posts with high comment-to-point ratio (controversial?)
jpx 'hits[?points > `100`] | [*].{
title: title,
points: points,
comments: num_comments,
ratio: divide(num_comments, points)
} | sort_by(@, &ratio) | reverse(@) | [:10]' hn_front.json
Question Posts (Seeking Help)
jpx 'hits[?ends_with(title, `?`)].{
title: title,
comments: num_comments
}' hn_front.json
Time-Based Analysis
Posts by Hour
jpx 'hits[*].{
title: title,
hour: split(created_at, `T`)[1] | split(@, `:`)[0]
} | group_by(@, &hour)' hn_front.json
Recent vs Older Content
jpx 'hits | {
recent: [?created_at_i > `1700000000`] | length(@),
older: [?created_at_i <= `1700000000`] | length(@)
}' hn_front.json
Author Analysis
Most Active Authors
jpx 'hits[*].author | frequencies(@) | items(@) | sort_by(@, &[1]) | reverse(@) | [:10]' hn_front.json
Author Vocabulary Fingerprint
# What words does a specific author use most?
jpx 'hits[?author == `dang`].title
| join(` `, @)
| tokens(@)
| remove_stopwords(@)
| frequencies(@)' hn_front.json
Cross-Dataset Comparisons
Compare Ask HN vs Show HN Vocabulary
# Run separately and compare results
jpx 'hits[*].title | join(` `, @) | tokens(@) | remove_stopwords(@) | stems(@) | frequencies(@)' hn_ask.json > ask_vocab.json
jpx 'hits[*].title | join(` `, @) | tokens(@) | remove_stopwords(@) | stems(@) | frequencies(@)' hn_show.json > show_vocab.json
Search Query Analysis
# Fetch multiple topics and compare
curl -s "https://hn.algolia.com/api/v1/search?query=kubernetes&hitsPerPage=30" > hn_k8s.json
curl -s "https://hn.algolia.com/api/v1/search?query=docker&hitsPerPage=30" > hn_docker.json
# Compare title vocabulary
jpx 'hits[*].title | join(` `, @) | tokens(@) | remove_stopwords(@) | frequencies(@)' hn_k8s.json
jpx 'hits[*].title | join(` `, @) | tokens(@) | remove_stopwords(@) | frequencies(@)' hn_docker.json
Building a Search Index
Use the NLP functions to prepare content for search:
# Create searchable document representations
jpx 'hits[:20] | [*].{
id: objectID,
title: title,
normalized_title: title | lower(@) | remove_accents(@),
title_tokens: title | tokens(@) | remove_stopwords(@) | stems(@),
has_text: story_text != null,
text_keywords: story_text
| default(@, `""`)
| regex_replace(@, `<[^>]+>`, ` `)
| tokens(@)
| remove_stopwords(@)
| stems(@)
| unique(@)
| slice(@, `0`, `20`)
}' hn_ask.json
Complete Analysis Pipeline
Here’s a comprehensive analysis combining multiple NLP techniques:
jpx '{
meta: {
total_stories: length(hits),
total_points: sum(hits[*].points),
avg_comments: avg(hits[*].num_comments)
},
top_keywords: hits[*].title
| join(` `, @)
| tokens(@)
| remove_stopwords(@)
| stems(@)
| frequencies(@)
| items(@)
| sort_by(@, &[1])
| reverse(@)
| [:10][*][0],
top_bigrams: hits[*].title
| join(` `, @)
| lower(@)
| bigrams(@)
| [*] | join(` `, @)
| frequencies(@)
| items(@)
| sort_by(@, &[1])
| reverse(@)
| [:5][*][0],
question_posts: length(hits[?ends_with(title, `?`)]),
avg_title_words: avg(hits[*].title | [*] | word_count(@))
}' hn_front.json
Using Query Libraries
Instead of typing these queries repeatedly, save them in a .jpx query library. See examples/hacker-news.jpx for a ready-to-use library:
# List available queries
jpx -Q examples/hacker-news.jpx --list-queries
# Run common analyses
jpx -Q examples/hacker-news.jpx:title-keywords hn_front.json
jpx -Q examples/hacker-news.jpx:top-stories hn_front.json
jpx -Q examples/hacker-news.jpx:summary hn_front.json
# Output as table
jpx -Q examples/hacker-news.jpx:most-discussed -t hn_front.json
See Query Files for more on creating and using query libraries.
Tips for HN Data
-
HTML in story_text: Always strip HTML tags before NLP processing
regex_replace(story_text, `<[^>]+>`, ` `) -
Rate limiting: The Algolia API is generous but cache results locally for iteration
-
Pagination: Use
pageparameter for more resultscurl "https://hn.algolia.com/api/v1/search?tags=ask_hn&page=2&hitsPerPage=50" -
Date filtering: Use
numericFiltersfor time rangescurl "https://hn.algolia.com/api/v1/search?numericFilters=created_at_i>1700000000" -
Combining with fuzzy search: Use jpx’s
fuzzy_searchon processed resultsjpx 'fuzzy_search(hits, `title`, `database`)' hn_front.json
Related Functions
- Text Functions -
tokens,stems,remove_stopwords,word_frequencies - NLP Pipelines - Complete NLP pipeline examples
- Regex Functions -
regex_replace,regex_matchfor HTML cleaning - Fuzzy Functions -
fuzzy_searchfor content discovery
USGS Earthquake Data
The USGS Earthquake Hazards Program provides real-time earthquake data via a public API. This dataset is excellent for learning jpx because it has:
- Nested GeoJSON structure
- Numeric data for statistical analysis (magnitude, depth)
- Geographic coordinates for geo functions
- Timestamps for date/time operations
Getting the Data
# Fetch recent magnitude 5+ earthquakes (10 events)
curl -s "https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&limit=10&minmagnitude=5" > earthquakes.json
# Fetch more data to play with (100 events)
curl -s "https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&limit=100&minmagnitude=4" > earthquakes_100.json
Data Structure
The API returns GeoJSON with this structure:
{
"type": "FeatureCollection",
"metadata": {
"generated": 1768410727000,
"title": "USGS Earthquakes",
"count": 10
},
"features": [
{
"type": "Feature",
"properties": {
"mag": 6.2,
"place": "133 km SE of Kuril'sk, Russia",
"time": 1768289648725,
"sig": 591,
"tsunami": 0,
"type": "earthquake",
"title": "M 6.2 - 133 km SE of Kuril'sk, Russia"
},
"geometry": {
"type": "Point",
"coordinates": [149.2768, 44.5495, 35.788]
},
"id": "us7000rpcg"
}
]
}
Key fields:
properties.mag- Magnitudeproperties.place- Human-readable locationproperties.time- Unix timestamp (milliseconds)properties.sig- Significance (0-1000+)geometry.coordinates- [longitude, latitude, depth_km]
Basic Queries
List All Earthquake Titles
jpx 'features[*].properties.title' earthquakes.json
Output:
[
"M 5.1 - 119 km N of Lae, Papua New Guinea",
"M 5.1 - 68 km E of Severo-Kuril'sk, Russia",
"M 6.2 - 133 km SE of Kuril'sk, Russia",
...
]
Get Magnitudes Only
jpx 'features[*].properties.mag' earthquakes.json
Output:
[5.1, 5.1, 6.2, 5.3, 5.6, 5.2, 5.2, 5.1, 5.0, 5.6]
Count Total Events
jpx 'length(features)' earthquakes.json
Output: 10
Filtering
Find Major Earthquakes (Magnitude 6+)
jpx 'features[?properties.mag >= `6`].properties.title' earthquakes.json
Output:
["M 6.2 - 133 km SE of Kuril'sk, Russia"]
Find Deep Earthquakes (> 100km depth)
The depth is the third element in the coordinates array:
jpx 'features[?geometry.coordinates[2] > `100`].{
title: properties.title,
depth_km: geometry.coordinates[2]
}' earthquakes.json
Output:
[
{"title": "M 5.1 - 119 km N of Lae, Papua New Guinea", "depth_km": 172.746},
{"title": "M 5.3 - 72 km W of Ollagüe, Chile", "depth_km": 118.039},
{"title": "M 5.1 - 233 km ENE of Lospalos, Timor Leste", "depth_km": 101.686}
]
Find Earthquakes with Tsunami Alerts
jpx 'features[?properties.tsunami == `1`].properties.title' earthquakes.json
Filter by Location (Text Match)
jpx 'features[?contains(properties.place, `Russia`)].properties.title' earthquakes.json
Output:
[
"M 5.1 - 68 km E of Severo-Kuril'sk, Russia",
"M 6.2 - 133 km SE of Kuril'sk, Russia",
"M 5.2 - 177 km S of Severo-Kuril'sk, Russia",
"M 5.6 - 265 km N of Kuril'sk, Russia"
]
Statistics
Average Magnitude
jpx 'avg(features[*].properties.mag)' earthquakes.json
Output: 5.34
Magnitude Statistics
jpx '{
count: length(features),
min: min(features[*].properties.mag),
max: max(features[*].properties.mag),
avg: avg(features[*].properties.mag),
median: median(features[*].properties.mag),
stddev: round(stddev(features[*].properties.mag), `3`)
}' earthquakes.json
Output:
{
"count": 10,
"min": 5.0,
"max": 6.2,
"avg": 5.34,
"median": 5.2,
"stddev": 0.351
}
Depth Statistics
jpx '{
shallowest: min(features[*].geometry.coordinates[2]),
deepest: max(features[*].geometry.coordinates[2]),
avg_depth: round(avg(features[*].geometry.coordinates[2]), `1`)
}' earthquakes.json
Sorting
Sort by Magnitude (Descending)
jpx 'sort_by(features, &properties.mag) | reverse(@) | [*].{
mag: properties.mag,
place: properties.place
}' earthquakes.json
Output:
[
{"mag": 6.2, "place": "133 km SE of Kuril'sk, Russia"},
{"mag": 5.6, "place": "Easter Island region"},
{"mag": 5.6, "place": "265 km N of Kuril'sk, Russia"},
...
]
Top 3 by Significance
jpx 'sort_by(features, &properties.sig) | reverse(@) | [:3] | [*].{
significance: properties.sig,
title: properties.title
}' earthquakes.json
Geographic Calculations
Extract Coordinates
jpx 'features[*].{
place: properties.place,
lat: geometry.coordinates[1],
lon: geometry.coordinates[0]
}' earthquakes.json
Distance from a Reference Point
Calculate distance from San Francisco (37.7749, -122.4194) using the geo_distance_km function:
jpx 'features[*].{
place: properties.place,
distance_km: round(geo_distance_km(
`37.7749`, `-122.4194`,
geometry.coordinates[1], geometry.coordinates[0]
), `0`)
} | sort_by(@, &distance_km)' earthquakes.json
Output:
[
{"place": "Easter Island region", "distance_km": 7580},
{"place": "72 km W of Ollagüe, Chile", "distance_km": 9513},
{"place": "South Sandwich Islands region", "distance_km": 12748},
...
]
Find Nearest Earthquake
jpx 'min_by(features, &geo_distance_km(
`37.7749`, `-122.4194`,
geometry.coordinates[1], geometry.coordinates[0]
)).properties.title' earthquakes.json
Date/Time Operations
Convert Timestamps to Readable Dates
The time field is Unix milliseconds. Convert to ISO format:
jpx 'features[*].{
title: properties.title,
date: from_unixtime(floor(divide(properties.time, `1000`)))
}' earthquakes.json
Find Earthquakes in Last 24 Hours
jpx --let now='now()' 'features[?properties.time > multiply(subtract($now, `86400`), `1000`)].properties.title' earthquakes.json
Group by Day
jpx 'features[*].{
day: format_datetime(from_unixtime(floor(divide(properties.time, `1000`))), `%Y-%m-%d`),
mag: properties.mag,
place: properties.place
}' earthquakes.json
Data Transformation
Reshape to Flat Structure
jpx 'features[*].{
id: id,
magnitude: properties.mag,
location: properties.place,
longitude: geometry.coordinates[0],
latitude: geometry.coordinates[1],
depth_km: geometry.coordinates[2],
timestamp: properties.time,
url: properties.url
}' earthquakes.json
Export to CSV
jpx --csv 'features[*].{
magnitude: properties.mag,
location: properties.place,
latitude: geometry.coordinates[1],
longitude: geometry.coordinates[0],
depth_km: geometry.coordinates[2]
}' earthquakes.json
Output:
magnitude,location,latitude,longitude,depth_km
5.1,"119 km N of Lae, Papua New Guinea",-5.6511,147.12,172.746
5.1,"68 km E of Severo-Kuril'sk, Russia",50.7928,157.0843,64.364
6.2,"133 km SE of Kuril'sk, Russia",44.5495,149.2768,35.788
...
Advanced Pipelines
Summary Report
Create a comprehensive earthquake summary:
jpx '{
report_title: `USGS Earthquake Summary`,
generated: metadata.title,
total_events: length(features),
magnitude_stats: {
min: min(features[*].properties.mag),
max: max(features[*].properties.mag),
average: round(avg(features[*].properties.mag), `2`),
median: median(features[*].properties.mag)
},
depth_stats: {
shallowest_km: min(features[*].geometry.coordinates[2]),
deepest_km: max(features[*].geometry.coordinates[2])
},
major_events: features[?properties.mag >= `5.5`] | [*].{
magnitude: properties.mag,
location: properties.place
},
by_region: {
russia: length(features[?contains(properties.place, `Russia`)]),
indonesia: length(features[?contains(properties.place, `Indonesia`)]),
chile: length(features[?contains(properties.place, `Chile`)])
}
}' earthquakes.json
Pipeline with Multiple Steps
# Get major earthquakes, sort by magnitude, format for display
jpx 'features
| [?properties.mag >= `5.5`]
| sort_by(@, &properties.mag)
| reverse(@)
| [*].{
mag: properties.mag,
where: properties.place,
depth: join(``, [to_string(geometry.coordinates[2]), ` km`])
}' earthquakes.json
Try It Yourself
-
Fetch live data: The USGS API updates in real-time
curl -s "https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&limit=50&minmagnitude=4" > quakes.json -
Explore the API parameters:
minmagnitude/maxmagnitude- Filter by magnitudestarttime/endtime- Date range (ISO 8601)latitude/longitude/maxradiuskm- Geographic radiuslimit- Number of results (max 20000)
-
API Documentation: USGS Earthquake API
Related Functions
geo_distance_km- Calculate distance between coordinatesavg,median,stddev- Statistical functionsfrom_unixtime- Convert timestampssort_by,min_by,max_by- Sorting functions
Nobel Prize Data
The Nobel Prize API provides comprehensive data about Nobel laureates and prizes. This dataset is excellent for learning jpx because it has:
- Deeply nested multilingual data
- Rich biographical information
- Dates and geographic locations
- Complex relationships (laureates, prizes, affiliations)
Getting the Data
# Fetch laureates (paginated, 25 per page by default)
curl -s "https://api.nobelprize.org/2.1/laureates?limit=50" > laureates.json
# Fetch all prizes
curl -s "https://api.nobelprize.org/2.1/nobelPrizes?limit=100" > prizes.json
# Fetch laureates by category
curl -s "https://api.nobelprize.org/2.1/laureates?nobelPrizeCategory=phy&limit=50" > physics_laureates.json
Data Structure
The laureates endpoint returns:
{
"laureates": [
{
"id": "102",
"knownName": {"en": "Aage N. Bohr", "se": "Aage N. Bohr"},
"givenName": {"en": "Aage N."},
"familyName": {"en": "Bohr"},
"gender": "male",
"birth": {
"date": "1922-06-19",
"place": {
"city": {"en": "Copenhagen"},
"country": {"en": "Denmark"},
"continent": {"en": "Europe"}
}
},
"death": {
"date": "2009-09-08",
"place": {...}
},
"nobelPrizes": [
{
"awardYear": "1975",
"category": {"en": "Physics"},
"portion": "1/3",
"motivation": {"en": "for the discovery of..."},
"prizeAmount": 630000,
"affiliations": [...]
}
]
}
],
"meta": {"count": 50, "limit": 50, "offset": 0}
}
Basic Queries
List All Laureate Names
jpx 'laureates[*].knownName.en' laureates.json
Get Name and Prize Year
jpx 'laureates[*].{
name: knownName.en,
year: nobelPrizes[0].awardYear,
category: nobelPrizes[0].category.en
}' laureates.json
Output:
[
{"name": "A. Michael Spence", "year": "2001", "category": "Economic Sciences"},
{"name": "Aage N. Bohr", "year": "1975", "category": "Physics"},
...
]
Count Laureates
jpx 'length(laureates)' laureates.json
Filtering
Find Physics Laureates
jpx 'laureates[?nobelPrizes[0].category.en == `Physics`].knownName.en' laureates.json
Find Female Laureates
jpx 'laureates[?gender == `female`].{
name: knownName.en,
category: nobelPrizes[0].category.en,
year: nobelPrizes[0].awardYear
}' laureates.json
Find Living Laureates
jpx 'laureates[?death == null].{
name: knownName.en,
born: birth.date
}' laureates.json
Find Laureates Born in a Specific Country
jpx 'laureates[?birth.place.country.en == `USA`].{
name: knownName.en,
city: birth.place.city.en
}' laureates.json
Find Multiple Prize Winners
Some laureates have won multiple Nobel Prizes:
jpx 'laureates[?length(nobelPrizes) > `1`].{
name: knownName.en,
prizes: nobelPrizes[*].{
year: awardYear,
category: category.en
}
}' laureates.json
Working with Multilingual Data
The API provides data in multiple languages (en, se, no):
Extract English Names
jpx 'laureates[*].knownName.en' laureates.json
Compare Languages
jpx 'laureates[:5] | [*].{
english: knownName.en,
swedish: knownName.se
}' laureates.json
Get Motivation in English
jpx 'laureates[*].{
name: knownName.en,
motivation: nobelPrizes[0].motivation.en
}' laureates.json
Date Operations
Extract Birth Years
jpx 'laureates[*].{
name: knownName.en,
birth_year: split(birth.date, `-`)[0]
}' laureates.json
Calculate Age at Award
jpx 'laureates[?birth.date != null].{
name: knownName.en,
born: split(birth.date, `-`)[0],
awarded: nobelPrizes[0].awardYear,
age_at_award: subtract(
to_number(nobelPrizes[0].awardYear),
to_number(split(birth.date, `-`)[0])
)
}' laureates.json
Find Laureates Born Before 1900
jpx 'laureates[?to_number(split(birth.date, `-`)[0]) < `1900`].{
name: knownName.en,
born: birth.date
}' laureates.json
Geographic Analysis
Laureates by Birth Country
jpx 'laureates[*].birth.place.country.en' laureates.json | jpx -s 'group_by(@, &@) | @.{
country: [0],
count: length(@)
}'
Or using frequencies:
jpx 'frequencies(laureates[*].birth.place.country.en)' laureates.json
Laureates by Continent
jpx 'laureates[*].{
name: knownName.en,
continent: birth.place.continent.en
}' laureates.json
Extract Coordinates
jpx 'laureates[?birth.place.cityNow.latitude != null].{
name: knownName.en,
city: birth.place.city.en,
lat: birth.place.cityNow.latitude,
lon: birth.place.cityNow.longitude
}' laureates.json
Prize Analysis
Prize Amounts Over Time
jpx 'laureates[*].nobelPrizes[0].{
year: awardYear,
amount: prizeAmount,
adjusted: prizeAmountAdjusted
} | sort_by(@, &year)' laureates.json
Shared Prizes
The portion field shows how the prize was split:
jpx 'laureates[?nobelPrizes[0].portion != `1`].{
name: knownName.en,
portion: nobelPrizes[0].portion,
year: nobelPrizes[0].awardYear
}' laureates.json
Find Solo Winners
jpx 'laureates[?nobelPrizes[0].portion == `1`].{
name: knownName.en,
category: nobelPrizes[0].category.en,
year: nobelPrizes[0].awardYear
}' laureates.json
Affiliations
Extract University Affiliations
jpx 'laureates[*].{
name: knownName.en,
university: nobelPrizes[0].affiliations[0].name.en,
country: nobelPrizes[0].affiliations[0].country.en
}' laureates.json
Find Stanford Affiliates
jpx 'laureates[?contains(to_string(nobelPrizes[*].affiliations[*].name.en), `Stanford`)].knownName.en' laureates.json
Text Processing
Search Motivations
Find laureates whose work involved “quantum”:
jpx 'laureates[?contains(lower(to_string(nobelPrizes[*].motivation.en)), `quantum`)].{
name: knownName.en,
motivation: nobelPrizes[0].motivation.en
}' laureates.json
Extract Key Terms from Motivations
jpx 'laureates[*].nobelPrizes[0].motivation.en' laureates.json
Data Transformation
Flatten for Export
jpx 'laureates[*].{
id: id,
name: knownName.en,
gender: gender,
birth_date: birth.date,
birth_country: birth.place.country.en,
death_date: death.date,
prize_year: nobelPrizes[0].awardYear,
prize_category: nobelPrizes[0].category.en,
prize_portion: nobelPrizes[0].portion,
motivation: nobelPrizes[0].motivation.en
}' laureates.json
Export to CSV
jpx --csv 'laureates[*].{
name: knownName.en,
year: nobelPrizes[0].awardYear,
category: nobelPrizes[0].category.en,
country: birth.place.country.en
}' laureates.json
Advanced Pipelines
Summary by Category
jpx '{
physics: length(laureates[?nobelPrizes[0].category.en == `Physics`]),
chemistry: length(laureates[?nobelPrizes[0].category.en == `Chemistry`]),
medicine: length(laureates[?contains(nobelPrizes[0].category.en, `Medicine`) || contains(nobelPrizes[0].category.en, `Physiology`)]),
literature: length(laureates[?nobelPrizes[0].category.en == `Literature`]),
peace: length(laureates[?nobelPrizes[0].category.en == `Peace`]),
economics: length(laureates[?contains(nobelPrizes[0].category.en, `Economic`)])
}' laureates.json
Gender Distribution by Decade
jpx 'laureates[*].{
decade: concat(slice(nobelPrizes[0].awardYear, `0`, `3`), `0s`),
gender: gender
}' laureates.json
Youngest and Oldest Winners
jpx '{
dataset: laureates[?birth.date != null] | [*].{
name: knownName.en,
age: subtract(to_number(nobelPrizes[0].awardYear), to_number(split(birth.date, `-`)[0]))
},
youngest: min_by(@.dataset, &age),
oldest: max_by(@.dataset, &age)
}' laureates.json
Try It Yourself
-
Explore the API:
# All laureates (paginated) curl -s "https://api.nobelprize.org/2.1/laureates?limit=100&offset=0" # Filter by category curl -s "https://api.nobelprize.org/2.1/laureates?nobelPrizeCategory=lit" # Filter by year curl -s "https://api.nobelprize.org/2.1/nobelPrizes?nobelPrizeYear=2023" -
Category codes:
phy(Physics),che(Chemistry),med(Medicine),lit(Literature),pea(Peace),eco(Economics) -
API Documentation: Nobel Prize API
Related Functions
contains,starts_with- Text matchingsplit,lower- String manipulationgroup_by,frequencies- Aggregationto_number,subtract- Calculations
NASA Near Earth Objects
NASA’s Near Earth Object Web Service provides data about asteroids and comets that pass close to Earth. This dataset is excellent for learning jpx because it has:
- Deeply nested data structures
- Multiple unit systems (km, miles, AU, lunar distances)
- Boolean flags for hazard classification
- Time series data (close approach dates)
- Complex orbital mechanics data
Getting the Data
# Browse all NEOs (paginated)
curl -s "https://api.nasa.gov/neo/rest/v1/neo/browse?api_key=DEMO_KEY" > neo_browse.json
# Get NEOs by close approach date (today)
curl -s "https://api.nasa.gov/neo/rest/v1/feed?api_key=DEMO_KEY" > neo_feed.json
# Get specific date range
curl -s "https://api.nasa.gov/neo/rest/v1/feed?start_date=2024-01-01&end_date=2024-01-07&api_key=DEMO_KEY" > neo_week.json
# Look up specific asteroid
curl -s "https://api.nasa.gov/neo/rest/v1/neo/2000433?api_key=DEMO_KEY" > eros.json
Note: Use
DEMO_KEYfor testing (limited rate). Get a free API key at api.nasa.gov.
Data Structure
The browse endpoint returns:
{
"links": {"self": "...", "next": "..."},
"page": {
"size": 20,
"total_elements": 41839,
"total_pages": 2092,
"number": 0
},
"near_earth_objects": [
{
"id": "2000433",
"name": "433 Eros (A898 PA)",
"name_limited": "Eros",
"nasa_jpl_url": "https://ssd.jpl.nasa.gov/...",
"absolute_magnitude_h": 10.38,
"estimated_diameter": {
"kilometers": {"estimated_diameter_min": 22.31, "estimated_diameter_max": 49.89},
"meters": {...},
"miles": {...},
"feet": {...}
},
"is_potentially_hazardous_asteroid": false,
"close_approach_data": [
{
"close_approach_date": "2024-01-15",
"relative_velocity": {
"kilometers_per_second": "5.57",
"kilometers_per_hour": "20083.02",
"miles_per_hour": "12478.81"
},
"miss_distance": {
"astronomical": "0.314",
"lunar": "122.5",
"kilometers": "47112732",
"miles": "29274494"
},
"orbiting_body": "Earth"
}
]
}
]
}
Basic Queries
List Asteroid Names
jpx 'near_earth_objects[*].name' neo_browse.json
Get Short Names
jpx 'near_earth_objects[*].name_limited' neo_browse.json
Output:
["Eros", "Albert", "Alinda", "Ganymed", ...]
Count NEOs in Response
jpx 'page.total_elements' neo_browse.json
Output: 41839 (total known NEOs!)
Filtering
Find Potentially Hazardous Asteroids (PHAs)
jpx 'near_earth_objects[?is_potentially_hazardous_asteroid == `true`].{
name: name_limited,
diameter_km: estimated_diameter.kilometers.estimated_diameter_max
}' neo_browse.json
Find Large Asteroids (> 1km)
jpx 'near_earth_objects[?estimated_diameter.kilometers.estimated_diameter_max > `1`].{
name: name,
max_diameter_km: estimated_diameter.kilometers.estimated_diameter_max,
hazardous: is_potentially_hazardous_asteroid
}' neo_browse.json
Find Non-Hazardous Objects
jpx 'near_earth_objects[?is_potentially_hazardous_asteroid == `false`].name_limited' neo_browse.json
Working with Nested Data
Extract Diameter Estimates
The API provides diameters in multiple units:
jpx 'near_earth_objects[*].{
name: name_limited,
min_km: estimated_diameter.kilometers.estimated_diameter_min,
max_km: estimated_diameter.kilometers.estimated_diameter_max
}' neo_browse.json
Calculate Average Diameter
jpx 'near_earth_objects[*].{
name: name_limited,
avg_diameter_km: divide(
add(
estimated_diameter.kilometers.estimated_diameter_min,
estimated_diameter.kilometers.estimated_diameter_max
),
`2`
)
}' neo_browse.json
Get Diameter in Different Units
jpx 'near_earth_objects[0].{
name: name,
in_km: estimated_diameter.kilometers,
in_meters: estimated_diameter.meters,
in_miles: estimated_diameter.miles
}' neo_browse.json
Close Approach Data
Get Next Close Approach
jpx 'near_earth_objects[*].{
name: name_limited,
next_approach: close_approach_data[0].close_approach_date,
miss_distance_km: close_approach_data[0].miss_distance.kilometers
}' neo_browse.json
Find Closest Approaches (in Lunar Distances)
One lunar distance = 384,400 km (distance to the Moon):
jpx 'near_earth_objects[*].close_approach_data[*].{
asteroid: @.@.@.name_limited,
date: close_approach_date,
lunar_distances: miss_distance.lunar
} | flatten(@)' neo_browse.json
Sort by Miss Distance
jpx 'near_earth_objects[*].{
name: name_limited,
miss_km: to_number(close_approach_data[0].miss_distance.kilometers)
} | sort_by(@, &miss_km) | [:10]' neo_browse.json
Find High-Speed Approaches
jpx 'near_earth_objects[*].{
name: name_limited,
speed_kph: to_number(close_approach_data[0].relative_velocity.kilometers_per_hour),
date: close_approach_data[0].close_approach_date
} | [?speed_kph > `50000`]' neo_browse.json
Statistics
Size Statistics
jpx '{
count: length(near_earth_objects),
smallest_km: min(near_earth_objects[*].estimated_diameter.kilometers.estimated_diameter_min),
largest_km: max(near_earth_objects[*].estimated_diameter.kilometers.estimated_diameter_max),
avg_max_km: round(avg(near_earth_objects[*].estimated_diameter.kilometers.estimated_diameter_max), `2`)
}' neo_browse.json
Hazard Statistics
jpx '{
total: length(near_earth_objects),
hazardous: length(near_earth_objects[?is_potentially_hazardous_asteroid == `true`]),
safe: length(near_earth_objects[?is_potentially_hazardous_asteroid == `false`]),
hazard_percentage: multiply(
divide(
length(near_earth_objects[?is_potentially_hazardous_asteroid == `true`]),
length(near_earth_objects)
),
`100`
)
}' neo_browse.json
Magnitude Distribution
Absolute magnitude (H) indicates brightness/size:
jpx '{
magnitudes: near_earth_objects[*].absolute_magnitude_h,
min_mag: min(near_earth_objects[*].absolute_magnitude_h),
max_mag: max(near_earth_objects[*].absolute_magnitude_h),
avg_mag: round(avg(near_earth_objects[*].absolute_magnitude_h), `2`)
}' neo_browse.json
Sorting and Ranking
Top 5 Largest Asteroids
jpx 'sort_by(near_earth_objects, &estimated_diameter.kilometers.estimated_diameter_max)
| reverse(@)
| [:5]
| [*].{
name: name,
max_diameter_km: round(estimated_diameter.kilometers.estimated_diameter_max, `2`),
hazardous: is_potentially_hazardous_asteroid
}' neo_browse.json
Brightest Objects (Lowest Magnitude)
jpx 'sort_by(near_earth_objects, &absolute_magnitude_h) | [:5] | [*].{
name: name_limited,
magnitude: absolute_magnitude_h,
diameter_km: estimated_diameter.kilometers.estimated_diameter_max
}' neo_browse.json
Unit Conversions
Convert AU to Kilometers
1 AU (Astronomical Unit) = ~149,597,870.7 km:
jpx 'near_earth_objects[*].{
name: name_limited,
miss_au: to_number(close_approach_data[0].miss_distance.astronomical),
miss_km: to_number(close_approach_data[0].miss_distance.kilometers)
}' neo_browse.json
Velocity Comparisons
jpx 'near_earth_objects[*].{
name: name_limited,
km_per_sec: to_number(close_approach_data[0].relative_velocity.kilometers_per_second),
km_per_hour: to_number(close_approach_data[0].relative_velocity.kilometers_per_hour),
mph: to_number(close_approach_data[0].relative_velocity.miles_per_hour)
}' neo_browse.json
Data Transformation
Flatten for Analysis
jpx 'near_earth_objects[*].{
id: id,
name: name_limited,
magnitude: absolute_magnitude_h,
diameter_min_km: estimated_diameter.kilometers.estimated_diameter_min,
diameter_max_km: estimated_diameter.kilometers.estimated_diameter_max,
is_hazardous: is_potentially_hazardous_asteroid,
next_approach_date: close_approach_data[0].close_approach_date,
miss_distance_km: close_approach_data[0].miss_distance.kilometers,
velocity_kph: close_approach_data[0].relative_velocity.kilometers_per_hour,
jpl_url: nasa_jpl_url
}' neo_browse.json
Export to CSV
jpx --csv 'near_earth_objects[*].{
name: name_limited,
magnitude: absolute_magnitude_h,
max_diameter_km: estimated_diameter.kilometers.estimated_diameter_max,
is_hazardous: is_potentially_hazardous_asteroid,
approach_date: close_approach_data[0].close_approach_date
}' neo_browse.json
Advanced Pipelines
Risk Assessment Report
jpx '{
report_title: `Near Earth Object Risk Assessment`,
total_objects: page.total_elements,
sample_size: length(near_earth_objects),
potentially_hazardous: {
count: length(near_earth_objects[?is_potentially_hazardous_asteroid == `true`]),
objects: near_earth_objects[?is_potentially_hazardous_asteroid == `true`] | [*].{
name: name,
diameter_km: round(estimated_diameter.kilometers.estimated_diameter_max, `2`),
next_approach: close_approach_data[0].close_approach_date,
miss_lunar: close_approach_data[0].miss_distance.lunar
}
},
largest_objects: sort_by(near_earth_objects, &estimated_diameter.kilometers.estimated_diameter_max)
| reverse(@)
| [:3]
| [*].{name: name_limited, km: round(estimated_diameter.kilometers.estimated_diameter_max, `1`)}
}' neo_browse.json
Pipeline: Filter, Sort, Transform
jpx 'near_earth_objects
| [?estimated_diameter.kilometers.estimated_diameter_max > `0.5`]
| [?is_potentially_hazardous_asteroid == `true`]
| sort_by(@, &estimated_diameter.kilometers.estimated_diameter_max)
| reverse(@)
| [*].{
name: name,
size_category: `LARGE PHA`,
diameter_range: concat(
to_string(round(estimated_diameter.kilometers.estimated_diameter_min, `1`)),
` - `,
to_string(round(estimated_diameter.kilometers.estimated_diameter_max, `1`)),
` km`
)
}' neo_browse.json
Working with the Feed Endpoint
The feed endpoint returns NEOs by date:
curl -s "https://api.nasa.gov/neo/rest/v1/feed?start_date=2024-01-15&end_date=2024-01-16&api_key=DEMO_KEY" > neo_day.json
Count by Date
jpx 'element_count' neo_day.json
List All Dates with Objects
jpx 'keys(near_earth_objects)' neo_day.json
Get Objects for Specific Date
jpx 'near_earth_objects."2024-01-15"[*].name' neo_day.json
Try It Yourself
-
Get your free API key: api.nasa.gov
-
Interesting queries to try:
- Find the closest approach ever recorded
- List all PHAs larger than 500 meters
- Calculate average velocity of approaching objects
-
API endpoints:
/neo/browse- Paginated list of all NEOs/neo/rest/v1/feed- NEOs by approach date/neo/{id}- Specific asteroid details
-
API Documentation: NASA NEO API
Related Functions
sort_by,reverse- Sorting and orderingto_number- Convert string numbersround,divide,multiply- Math operationsavg,min,max- Statisticsflatten- Flatten nested arrays
Project Management Dataset
A comprehensive example using synthetic project management data that demonstrates most jpx function categories.
Dataset
All examples use this project management dataset:
[
{
"projectId": "P-8801",
"status": "active",
"timestamps": { "created_at": "2025-01-15T08:30:00Z", "last_modified": "2025-12-19T09:05:00Z" },
"details": {
"name": "Quantum Migration",
"description": "High-priority initiative to transition legacy on-premise infrastructure to a distributed cloud-native architecture.",
"tags": ["cloud", "security", "infrastructure"]
},
"milestones": [{ "phase": "Discovery", "due_date": "2025-03-01", "completed": true }]
},
{
"projectId": "P-9902",
"status": "pending",
"timestamps": { "created_at": "2025-06-10T14:20:00Z", "last_modified": "2025-11-30T11:00:00Z" },
"details": {
"name": "Mobile App Redesign",
"description": "Revamping the user interface for the primary customer-facing mobile application to improve accessibility and engagement.",
"tags": ["UX", "frontend", "accessibility"]
},
"milestones": []
},
{
"projectId": "P-4405",
"status": "archived",
"timestamps": { "created_at": "2024-11-01T09:00:00Z", "last_modified": "2025-01-20T16:30:00Z" },
"details": {
"name": "Legacy Data Cleanup",
"description": "Automated purging of obsolete database records older than seven years to maintain compliance with regional data privacy laws.",
"tags": ["database", "compliance", "automation"]
},
"milestones": [{ "phase": "Archive Audit", "due_date": "2025-01-15", "completed": true }]
},
{
"projectId": "P-5521",
"status": "active",
"timestamps": { "created_at": "2025-02-14T10:15:00Z", "last_modified": "2025-12-10T14:45:00Z" },
"details": {
"name": "Cyber Sentinel Firewall",
"description": "Implementation of AI-driven threat detection systems to safeguard corporate networks against zero-day vulnerabilities.",
"tags": ["AI", "security", "network"]
},
"milestones": [{ "phase": "Training", "due_date": "2025-05-20", "completed": true }]
},
{
"projectId": "P-1033",
"status": "on-hold",
"timestamps": { "created_at": "2025-03-22T13:00:00Z", "last_modified": "2025-09-05T08:20:00Z" },
"details": {
"name": "Blockchain Supply Chain",
"description": "Integrating distributed ledger technology to provide end-to-end transparency for global logistics and manufacturing pipelines.",
"tags": ["blockchain", "logistics", "transparency"]
},
"milestones": []
},
{
"projectId": "P-2294",
"status": "active",
"timestamps": { "created_at": "2025-04-12T16:45:00Z", "last_modified": "2025-12-18T10:00:00Z" },
"details": {
"name": "Green Energy Analytics",
"description": "Analyzing power consumption patterns to optimize the efficiency of solar and wind-based energy grids.",
"tags": ["sustainability", "analytics", "energy"]
},
"milestones": [{ "phase": "Sensor Install", "due_date": "2025-08-30", "completed": true }]
},
{
"projectId": "P-7761",
"status": "pending",
"timestamps": { "created_at": "2025-05-19T11:20:00Z", "last_modified": "2025-05-19T11:20:00Z" },
"details": {
"name": "Internal Wiki Overhaul",
"description": "Restructuring the company knowledge base to facilitate better cross-departmental collaboration and documentation sharing.",
"tags": ["knowledge", "internal", "documentation"]
},
"milestones": []
},
{
"projectId": "P-6610",
"status": "active",
"timestamps": { "created_at": "2025-07-04T09:00:00Z", "last_modified": "2025-12-15T15:30:00Z" },
"details": {
"name": "E-Commerce Personalization",
"description": "Deploying machine learning models to provide customized product recommendations based on real-time user behavior.",
"tags": ["retail", "ML", "personalization"]
},
"milestones": [{ "phase": "Model Alpha", "due_date": "2025-11-01", "completed": true }]
},
{
"projectId": "P-3349",
"status": "archived",
"timestamps": { "created_at": "2024-08-15T14:10:00Z", "last_modified": "2025-03-12T12:00:00Z" },
"details": {
"name": "Legacy Email Migration",
"description": "Relocating historical email archives from physical tape drives to a searchable secure cloud vault.",
"tags": ["email", "storage", "archival"]
},
"milestones": [{ "phase": "Final Sync", "due_date": "2025-02-28", "completed": true }]
},
{
"projectId": "P-1122",
"status": "active",
"timestamps": { "created_at": "2025-09-30T08:45:00Z", "last_modified": "2025-12-19T11:15:00Z" },
"details": {
"name": "HR Portal Integration",
"description": "Syncing payroll and performance management software to create a unified dashboard for human resources staff.",
"tags": ["HR", "integration", "software"]
},
"milestones": [{ "phase": "API Bridge", "due_date": "2026-01-10", "completed": false }]
}
]
Basic Extraction
Get all values for a field
# All project IDs
[*].projectId
["P-8801", "P-9902", "P-4405", "P-5521", "P-1033", "P-2294", "P-7761", "P-6610", "P-3349", "P-1122"]
Navigate nested objects
# All project names
[*].details.name
["Quantum Migration", "Mobile App Redesign", "Legacy Data Cleanup", "Cyber Sentinel Firewall", "Blockchain Supply Chain", "Green Energy Analytics", "Internal Wiki Overhaul", "E-Commerce Personalization", "Legacy Email Migration", "HR Portal Integration"]
Access by index
# First project's name
[0].details.name
"Quantum Migration"
Filtering
Simple equality filter
# Active projects only
[?status == 'active'].details.name
["Quantum Migration", "Cyber Sentinel Firewall", "Green Energy Analytics", "E-Commerce Personalization", "HR Portal Integration"]
Numeric comparison
# Projects with milestones
[?length(milestones) > `0`].projectId
["P-8801", "P-4405", "P-5521", "P-2294", "P-6610", "P-3349", "P-1122"]
Empty array check
# Projects with no milestones
[?milestones == `[]`].details.name
["Mobile App Redesign", "Blockchain Supply Chain", "Internal Wiki Overhaul"]
Nested filtering
# All incomplete milestones across all projects
[*].milestones[?completed == `false`][]
[{"phase": "API Bridge", "due_date": "2026-01-10", "completed": false}]
Flattening & Nested Access
Flatten nested arrays
# All tags across all projects (flattened)
[*].details.tags[]
["cloud", "security", "infrastructure", "UX", "frontend", "accessibility", "database", "compliance", "automation", "AI", "security", "network", "blockchain", "logistics", "transparency", "sustainability", "analytics", "energy", "knowledge", "internal", "documentation", "retail", "ML", "personalization", "email", "storage", "archival", "HR", "integration", "software"]
Get unique values
# Unique tags (deduplicated and sorted)
unique([*].details.tags[]) | sort(@)
["AI", "HR", "ML", "UX", "accessibility", "analytics", "archival", "automation", "blockchain", "cloud", "compliance", "database", "documentation", "email", "energy", "frontend", "infrastructure", "integration", "internal", "knowledge", "logistics", "network", "personalization", "retail", "security", "software", "storage", "sustainability", "transparency"]
Status & Grouping
Count by category
# Count projects by status
frequencies([*].status)
{"active": 5, "pending": 2, "archived": 2, "on-hold": 1}
Group into buckets
# Group projects by status
group_by(@, 'status') | keys(@)
["active", "archived", "on-hold", "pending"]
Tag Operations
Tag frequency analysis
# How often each tag appears
frequencies([*].details.tags[])
{"security": 2, "cloud": 1, "infrastructure": 1, "UX": 1, "frontend": 1, ...}
Find projects by tag
# Projects with "security" tag
[?includes(details.tags, 'security')].{id: projectId, name: details.name}
[
{"id": "P-8801", "name": "Quantum Migration"},
{"id": "P-5521", "name": "Cyber Sentinel Firewall"}
]
Multiple tag filter (AND)
# Projects with BOTH security AND cloud tags
[?includes(details.tags, 'security') && includes(details.tags, 'cloud')].projectId
["P-8801"]
Tags as comma-separated string
# All unique tags as one string
join(', ', unique([*].details.tags[]) | sort(@))
"AI, HR, ML, UX, accessibility, analytics, archival, automation, blockchain, cloud, compliance, database, documentation, email, energy, frontend, infrastructure, integration, internal, knowledge, logistics, network, personalization, retail, security, software, storage, sustainability, transparency"
Date & Time Queries
Filter by date prefix
# Projects created in 2025
[?starts_with(timestamps.created_at, '2025')].projectId
["P-8801", "P-9902", "P-5521", "P-1033", "P-2294", "P-7761", "P-6610", "P-1122"]
Find most recent
# Most recently modified project
max_by(@, ×tamps.last_modified).details.name
"HR Portal Integration"
Find oldest
# Oldest project by creation date
min_by(@, ×tamps.created_at).details.name
"Legacy Email Migration"
Human-readable time ago
# Time since last modification (human readable)
[*].{name: details.name, last_touch: time_ago(timestamps.last_modified)}
[
{"name": "Quantum Migration", "last_touch": "1 hour ago"},
{"name": "Mobile App Redesign", "last_touch": "19 days ago"},
...
]
Stale project detection
# Projects not modified in 30+ days
[?date_diff(now(), to_epoch(timestamps.last_modified), 'days') > `30`].details.name
["Mobile App Redesign", "Blockchain Supply Chain", "Internal Wiki Overhaul", "Legacy Email Migration"]
Same-day check
# Projects modified today
[?is_same_day(timestamps.last_modified, from_epoch(now()))].details.name
["Quantum Migration", "HR Portal Integration"]
Milestone Analysis
Projects without milestones
# Projects with no milestones defined
[?milestones == `[]`].{id: projectId, name: details.name, status: status}
[
{"id": "P-9902", "name": "Mobile App Redesign", "status": "pending"},
{"id": "P-1033", "name": "Blockchain Supply Chain", "status": "on-hold"},
{"id": "P-7761", "name": "Internal Wiki Overhaul", "status": "pending"}
]
Find incomplete work
# Projects with pending milestones
[?milestones[?completed == `false`]].{
project: details.name,
pending: milestones[?completed == `false`][].phase
}
[{"project": "HR Portal Integration", "pending": ["API Bridge"]}]
Upcoming milestones
# All incomplete milestones with due dates
[*].milestones[?completed == `false`][].{phase: phase, due: due_date}
[{"phase": "API Bridge", "due": "2026-01-10"}]
Text & Search
Fuzzy name search
# Fuzzy match project names
[?fuzzy_match(details.name, 'energy')].details.name
["Green Energy Analytics"]
Keyword search in descriptions
# Projects mentioning AI or machine learning
[?contains(details.description, 'AI') || contains(details.description, 'machine learning')].details.name
["Cyber Sentinel Firewall", "E-Commerce Personalization"]
Case-insensitive search
# Projects mentioning compliance (case-insensitive)
[?contains(lower(details.description), 'compliance')].details.name
["Legacy Data Cleanup"]
Word analysis
# Word count in descriptions
[*].{name: details.name, words: length(words(details.description))}
[
{"name": "Quantum Migration", "words": 14},
{"name": "Mobile App Redesign", "words": 16},
...
]
Truncated summaries
# Shortened descriptions
[*].{name: details.name, summary: truncate(details.description, `50`)}
[
{"name": "Quantum Migration", "summary": "High-priority initiative to transition legacy..."},
...
]
String Transformations
URL-friendly slugs
# Kebab-case project names for URLs
[*].{id: projectId, slug: kebab_case(details.name)}
[
{"id": "P-8801", "slug": "quantum-migration"},
{"id": "P-9902", "slug": "mobile-app-redesign"},
...
]
Database column names
# Snake-case for database columns
[*].details.name | map(&snake_case(@), @)
["quantum_migration", "mobile_app_redesign", "legacy_data_cleanup", ...]
Uppercase status
# Uppercase status field
[*].{name: details.name, status: upper(status)}
[
{"name": "Quantum Migration", "status": "ACTIVE"},
...
]
Projections & Reshaping
Create summary objects
# Clean summary for each project
[*].{id: projectId, name: details.name, status: status}
[
{"id": "P-8801", "name": "Quantum Migration", "status": "active"},
{"id": "P-9902", "name": "Mobile App Redesign", "status": "pending"},
...
]
Computed fields
# Add computed milestone count
[*].{
name: details.name,
status: status,
milestone_count: length(milestones),
has_incomplete: length(milestones[?completed == `false`]) > `0`
}
[
{"name": "Quantum Migration", "status": "active", "milestone_count": 1, "has_incomplete": false},
{"name": "HR Portal Integration", "status": "active", "milestone_count": 1, "has_incomplete": true},
...
]
Aggregation & Analytics
Dashboard summary
# Executive dashboard
{
total_projects: length(@),
active: length([?status == 'active']),
pending: length([?status == 'pending']),
on_hold: length([?status == 'on-hold']),
archived: length([?status == 'archived']),
with_milestones: length([?milestones != `[]`]),
without_milestones: length([?milestones == `[]`]),
incomplete_milestones: length([*].milestones[?completed == `false`][]),
newest_project: max_by(@, ×tamps.created_at).details.name,
oldest_project: min_by(@, ×tamps.created_at).details.name,
unique_tags: length(unique([*].details.tags[]))
}
{
"total_projects": 10,
"active": 5,
"pending": 2,
"on_hold": 1,
"archived": 2,
"with_milestones": 7,
"without_milestones": 3,
"incomplete_milestones": 1,
"newest_project": "HR Portal Integration",
"oldest_project": "Legacy Email Migration",
"unique_tags": 29
}
Complex Filters
Multiple conditions
# Active projects with completed milestones, modified this month
[?status == 'active'
&& milestones[?completed == `true`]
&& starts_with(timestamps.last_modified, '2025-12')
].details.name
["Quantum Migration", "Cyber Sentinel Firewall", "Green Energy Analytics", "E-Commerce Personalization", "HR Portal Integration"]
Stale pending projects
# Pending projects with no recent activity (60+ days)
[?status == 'pending'
&& date_diff(now(), to_epoch(timestamps.last_modified), 'days') > `60`
].details.name
["Internal Wiki Overhaul"]
At-risk detection
# Active projects without any milestones (potential risk)
[?status == 'active' && milestones == `[]`].details.name
[]
Export-Ready Formats
CSV row generation
# Generate CSV rows
[*] | map(&to_csv([projectId, status, details.name]), @)
[
"P-8801,active,Quantum Migration",
"P-9902,pending,Mobile App Redesign",
...
]
Bullet list for reports
# Active project names as bullet list
[?status == 'active'].details.name | join('\n- ', @)
"Quantum Migration\n- Cyber Sentinel Firewall\n- Green Energy Analytics\n- E-Commerce Personalization\n- HR Portal Integration"
Full export with metadata
# Rich export format
[*].{
id: projectId,
title: details.name,
status: upper(status),
tags: join(', ', details.tags),
age_days: date_diff(now(), to_epoch(timestamps.created_at), 'days'),
last_touch: time_ago(timestamps.last_modified),
slug: kebab_case(details.name)
}
[
{
"id": "P-8801",
"title": "Quantum Migration",
"status": "ACTIVE",
"tags": "cloud, security, infrastructure",
"age_days": 338,
"last_touch": "1 hour ago",
"slug": "quantum-migration"
},
...
]
Function Categories Used
This guide demonstrates functions from these categories:
| Category | Functions Used |
|---|---|
| Array | first, last, unique, length, frequencies, group_by |
| String | upper, lower, join, split, starts_with, contains, truncate, kebab_case, snake_case, words |
| DateTime | now, to_epoch, from_epoch, time_ago, date_diff, is_same_day |
| Expression | map, filter_expr, max_by, min_by |
| Utility | includes, default |
| Fuzzy | fuzzy_match |
| Format | to_csv |
Try It Yourself
Save the sample dataset to a file and experiment:
# Save the JSON to a file
cat > projects.json << 'EOF'
[ ... paste the sample data ... ]
EOF
# Run queries with jpx
cat projects.json | jpx '[?status == '\''active'\''].details.name'
cat projects.json | jpx 'frequencies([*].status)'
cat projects.json | jpx '[*].{name: details.name, tags: join('\'', '\'', details.tags)}'
Or use the interactive REPL:
jpx --repl < projects.json