Tidak suka iklan? Pergi Bebas Iklan Hari ini

jq One-Liners Filter and Transform JSON from the CLI Without Writing a Script

Diperbarui pada

Seven practical jq recipes for backend devs and DevOps — covering select, map, to_entries, del, group_by, and real-world API reshaping. Input and output shown for every pattern.

jq One-Liners: Filter and Transform JSON from the CLI Without Writing a Script 1
IKLAN · HAPUS?

You’re looking at a minified API response in your terminal. You need to extract 3 fields, filter out the nulls, and pipe the result to something else. Writing a Python script for this is 20 lines and 2 minutes you don’t have. jq handles it in one command — if you know the patterns.

Seven recipes below. Each one covers a pattern you’ll hit repeatedly when working with API responses or log files. Input, command, and output shown for every recipe.

Install

brew install jq        # macOS
apt-get install jq     # Ubuntu/Debian
apk add jq             # Alpine (Docker images)

Recipe 1: Extract a Field from Every Object in an Array

The most common thing you’ll do with jq: pull one field from every item in a JSON array.

Input (users.json)

[
  {"id": 1, "name": "Alice", "email": "alice@example.com", "role": "admin"},
  {"id": 2, "name": "Bob",   "email": "bob@example.com",   "role": "user"},
  {"id": 3, "name": "Carol", "email": "carol@example.com", "role": "user"}
]
jq '.[].name' users.json
# → "Alice"
#   "Bob"
#   "Carol"  (newline-separated stream)

.[] iterates over every element in the array; .name plucks the field. The output is a stream — useful for piping to other commands. If you need a proper JSON array back:

jq 'map(.name)' users.json
# → ["Alice", "Bob", "Carol"]

map(.name) adalah singkatan dari [.[] | .name] — it applies the expression to each item and wraps the results in an array. You’ll use map() a lot.

Recipe 2: Filter with select

Keep only the objects that match a condition; drop the rest.

jq 'map(select(.role == "admin"))' users.json
[
  {"id": 1, "name": "Alice", "email": "alice@example.com", "role": "admin"}
]

select(expr) passes the item through only when the expression is truthy — everything else disappears. Wrapping it in map() keeps the output as an array. More examples:

# Numeric comparison
jq 'map(select(.score > 80))' results.json

# Not-null check
jq 'map(select(.error != null))' events.json

# String contains (requires test)
jq 'map(select(.message | test("timeout")))' logs.json

Recipe 3: Reshape Objects with map

Pick only the fields you need and optionally rename them in the same pass.

jq 'map({name: .name, role: .role})' users.json
[
  {"name": "Alice", "role": "admin"},
  {"name": "Bob",   "role": "user"},
  {"name": "Carol", "role": "user"}
]

You’re building a new object literal for each item. To rename a key, change the left side of the colon:

jq 'map({username: .name, access_level: .role})' users.json

One shorthand that saves keystrokes: {name} is equivalent to {name: .name}. If you want a field as-is, you don’t have to write the key twice:

jq 'map({id, name, role})' users.json

Recipe 4: Rename Keys with to_entries dan from_entries

map is good when you know which keys exist. When you need to transform the keys themselves — add a prefix, convert naming conventions, or rename based on a lookup — to_entries is the right pattern.

Input (config.json)

{"api_url": "https://api.example.com", "timeout": 30, "retry_count": 3}
# Add cfg_ prefix to every key
jq 'to_entries | map(.key = "cfg_" + .key) | from_entries' config.json
{"cfg_api_url": "https://api.example.com", "cfg_timeout": 30, "cfg_retry_count": 3}

to_entries turns {"k": "v"} ke dalam [{"key": "k", "value": "v"}]. After you mutate the entries in the resulting array, from_entries reconstructs the object. To rename one specific key without touching the others:

jq 'to_entries | map(if .key == "api_url" then .key = "endpoint" else . end) | from_entries' config.json

Recipe 5: Strip Fields with del

The inverse of reshaping: keep everything and remove a few specific fields. The main use case is stripping sensitive data before logging or before passing a payload to a third-party service.

jq 'map(del(.email))' users.json
[
  {"id": 1, "name": "Alice", "role": "admin"},
  {"id": 2, "name": "Bob",   "role": "user"},
  {"id": 3, "name": "Carol", "role": "user"}
]

Delete multiple fields at once, or delete nested paths:

# Multiple top-level fields at once
jq 'del(.password, .token, .refresh_token)' user.json

# Nested path
jq 'del(.user.internal_id)' response.json

# All fields named "debug" at any depth (recursive descent)
jq 'del(.. | .debug? // empty)' response.json

Recipe 6: Filter, Reshape, and Sort in One Pass

The real payoff is chaining these primitives. Here’s a pattern that shows up constantly when working with paginated API responses: descend into a nested array, filter it, reshape the objects, and sort the result.

Input (repos.json)

{
  "total_count": 3,
  "items": [
    {"id": 1, "name": "repo-alpha", "stargazers_count": 142, "language": "Go",     "private": false},
    {"id": 2, "name": "repo-beta",  "stargazers_count": 89,  "language": "Python", "private": true},
    {"id": 3, "name": "repo-gamma", "stargazers_count": 310, "language": "Go",     "private": false}
  ]
}
jq '.items
  | map(select(.private == false and .language == "Go"))
  | sort_by(-.stargazers_count)
  | map({name, stars: .stargazers_count})' repos.json
[
  {"name": "repo-gamma", "stars": 310},
  {"name": "repo-alpha", "stars": 142}
]

The pipeline:

  • .items — descend into the nested array
  • map(select(...)) — filter in a single pass; and chains conditions
  • sort_by(-.stargazers_count) — negate the value for descending sort; sort_by(.field) alone gives ascending
  • map({name, stars: .stargazers_count}) — final reshape; {name} adalah singkatan dari {name: .name}

Recipe 7: Count Log Levels with group_by

Frequency analysis on structured log output — no database needed, no awk gymnastics.

Input (logs.json)

[
  {"level": "error", "msg": "connection timeout",     "service": "auth"},
  {"level": "info",  "msg": "request received",       "service": "api"},
  {"level": "error", "msg": "null pointer exception", "service": "worker"},
  {"level": "warn",  "msg": "slow query detected",    "service": "db"},
  {"level": "error", "msg": "rate limit exceeded",    "service": "api"}
]
jq 'group_by(.level) | map({level: .[0].level, count: length}) | sort_by(-.count)' logs.json
[
  {"level": "error", "count": 3},
  {"level": "info",  "count": 1},
  {"level": "warn",  "count": 1}
]

group_by(.level) returns an array of arrays — each sub-array holds all entries that share the same level value. .[0].level grabs the level name from the first item in the group; length counts how many entries are in the group.

Add a per-service breakdown in the same query:

jq 'group_by(.level) | map({
  level:    .[0].level,
  count:    length,
  services: map(.service) | unique
})' logs.json

Rincian Singkat

The patterns above in table form for when you need a quick reminder:

What you wantjq expression
All values of a fieldmap(.field)
Filter matching itemsmap(select(.field == "val"))
Reshape each objectmap({newKey: .oldKey})
Rename all keysto_entries | map(.key = ...) | from_entries
Remove fieldsmap(del(.field1, .field2))
Sort ascendingsort_by(.field)
Sort descendingsort_by(-.field)
Count by groupgroup_by(.field) | map({key: .[0].field, count: length})
Get all keys of an objectkeys
Count items in arraylength
Unique values of a fieldmap(.field) | unique

Before You Start: Tame the Blob

API responses often come back minified or deeply nested. If you’re not sure of the path you need, paste the JSON into the Pemformat JSON — it pretty-prints with collapsible nodes so you can spot the path before writing the jq expression. After running a transformation, the Bandingkan JSON tool is useful for verifying the before/after diff when you’re reshaping or stripping fields and want to confirm nothing unexpected changed.

These seven patterns cover the bulk of JSON wrangling you’ll do at the command line. The real power is chaining them — once you can filter, reshape, and sort, you can process most API responses without touching a script file.

Ingin bebas iklan? Bebas Iklan Hari Ini

Instal Ekstensi Kami

Tambahkan alat IO ke browser favorit Anda untuk akses instan dan pencarian lebih cepat

Ke Ekstensi Chrome Ke Ekstensi Tepi Ke Ekstensi Firefox Ke Ekstensi Opera

Papan Skor Telah Tiba!

Papan Skor adalah cara yang menyenangkan untuk melacak permainan Anda, semua data disimpan di browser Anda. Lebih banyak fitur akan segera hadir!

IKLAN · HAPUS?
IKLAN · HAPUS?
IKLAN · HAPUS?

Pojok Berita dengan Sorotan Teknologi

Terlibat

Bantu kami untuk terus menyediakan alat gratis yang berharga

Belikan aku kopi
IKLAN · HAPUS?