---
title: "JSON / jq Recipes Cheatsheet"
slug: "json-jq-recipes"
category: "cheatsheets"
tags: ["cheatsheets", "jq", "json", "cli", "data-transformation", "bash"]
status: "stable"
last_updated: 2026-05-14
summary: "jq recipes for common transforms: select, map, group_by, conditional output, batch updates, nested filtering, and output reshaping."
related:
  [
    "[[cheatsheets/jq-syntax]]",
    "[[cheatsheets/curl-test-patterns]]",
    "[[cheatsheets/bash-one-liners]]",
    "[[cheatsheets/find-grep-awk-sed]]",
    "[[backend/postgres]]",
    "[[cheatsheets/http-status-codes]]",
    "[[cheatsheets/index|Cheatsheets]]",
  ]
---

## Overview

`jq` is a streaming JSON processor. It reads JSON from stdin or files, applies a filter expression, and writes JSON (or plain text) to stdout. This card is a recipe book for the transformations that come up repeatedly in API scripting, data pipelines, and config manipulation. For the core syntax vocabulary see [[cheatsheets/jq-syntax]]; for getting the JSON in the first place see [[cheatsheets/curl-test-patterns]].

## Basic extraction

| Task | Filter | Input | Output |
|---|---|---|---|
| Top-level field | `.name` | `{"name":"Alice"}` | `"Alice"` |
| Nested field | `.address.city` | `{"address":{"city":"NYC"}}` | `"NYC"` |
| Array index | `.[2]` | `[0,1,2,3]` | `2` |
| Last element | `.[-1]` | `[1,2,3]` | `3` |
| Slice | `.[1:3]` | `[0,1,2,3]` | `[1,2]` |
| Keys of object | `keys` | `{"a":1,"b":2}` | `["a","b"]` |
| Values of object | `[.[]]` | `{"a":1,"b":2}` | `[1,2]` |
| Length | `length` | `[1,2,3]` or `"hello"` | `3` or `5` |
| Type | `type` | `[1,2]` | `"array"` |
| Raw string output | `jq -r '.name'` | `{"name":"Alice"}` | `Alice` (no quotes) |

Use `-r` (raw output) when passing the result to a shell variable: `NAME=$(jq -r '.name' data.json)`.

## Filtering arrays

| Task | Filter | Notes |
|---|---|---|
| Filter by field value | `.[] \| select(.status == "active")` | Outputs each matching object |
| Filter by existence | `.[] \| select(.email != null)` | Skip objects missing a field |
| Filter by type | `.[] \| select(type == "object")` | Skip non-objects in mixed arrays |
| First match | `first(.[] \| select(.active))` | Stop after first |
| Count matches | `[.[] \| select(.active)] \| length` | Wrap in `[]` before `length` |
| Reject items | `.[] \| select(.status != "deleted")` | Inverse filter |
| Filter nested array | `.users[] \| select(.roles[] == "admin")` | Any role matches |

Chain `select` after `map` to filter and transform in one pass: `[.[] | select(.score > 80) | .name]`.

## Transforming and reshaping

| Task | Filter | Notes |
|---|---|---|
| Map over array | `[.[] \| .name]` or `map(.name)` | Extract field from each item |
| Map with expression | `map(.price * .qty)` | Compute derived value |
| Add field | `map(. + {total: (.price * .qty)})` | Augment each object |
| Remove field | `map(del(.internal))` | Delete key from each |
| Rename field | `map({id: .uid, name: .name})` | Build new object |
| Flatten one level | `flatten(1)` | Nested arrays |
| Flatten all | `flatten` | Fully flat |
| Unique values | `[.[] \| .tag] \| unique` | Deduplicate |
| Sort by field | `sort_by(.created_at)` | Ascending; `reverse` for desc |
| Group by field | `group_by(.category)` | Array of arrays |

`map(f)` is shorthand for `[.[] | f]`. Prefer `map` for readability.

## Conditional output

| Task | Filter | Notes |
|---|---|---|
| If-then-else | `.status \| if . == "ok" then "pass" else "fail" end` | Value branch |
| Null default | `.nickname // "anon"` | Operator `//` is alternative |
| Optional field | `.address?.city` | `?` suppresses error if null |
| Try-catch | `try .x catch "missing"` | Catches errors |
| When matching | `if .type == "user" then .email else empty end` | `empty` produces no output |

The alternative operator `//` returns the right side when the left is `false` or `null`. It does not trigger on `0` or `""`.

## Building output formats

| Task | Filter | Notes |
|---|---|---|
| CSV row | `[.name, .email] \| @csv` | Quoted CSV string |
| TSV row | `[.name, .email] \| @tsv` | Tab-separated |
| URL-encoded | `.value \| @uri` | Percent-encode a string |
| HTML-escape | `.html \| @html` | Escapes `<>&` |
| Base64 encode | `.data \| @base64` | Standard base64 |
| Base64 decode | `.encoded \| @base64d` | Decode |
| Shell-safe | `.value \| @sh` | Quote for shell embedding |
| JSON string | `. \| tojson` | Emit as JSON string |
| Parse JSON string | `.raw \| fromjson` | Parse embedded JSON string |

Combine with `-r` for raw output: `jq -r '[.name,.email] | @csv'` writes a CSV line without outer quotes.

## Batch and file operations

| Task | Command | Notes |
|---|---|---|
| Process multiple files | `jq '.name' file1.json file2.json` | Each file processed in turn |
| Merge two objects | `jq -s '.[0] * .[1]' base.json patch.json` | `-s` slurps into array |
| Concatenate arrays | `jq -s 'add' part1.json part2.json` | `add` reduces with `+` |
| Update in place | `jq '.version = "2.0"' pkg.json > tmp && mv tmp pkg.json` | jq has no in-place flag |
| Stream large file | `jq -c '.[]' large.json` | `-c` compact; one line per item |
| Null-separated | `jq -j` | Omit newline between outputs |
| Read from variable | `echo "$JSON" \| jq '.name'` | Pipe string to jq |

jq has no `--in-place` flag. Write to a temp file and rename. `sponge` from `moreutils` can simplify this: `jq '.x = 1' f.json \| sponge f.json`.

## Common gotchas

- `jq` outputs JSON by default, so strings are double-quoted. Use `-r` when passing values to shell.
- `.foo` on a `null` returns `null` silently. Use `select(. != null)` upstream to catch unexpected nulls.
- `//` is the alternative operator, not a comment. Comments are not supported in jq filters.
- `map(f)` and `[.[] | f]` are identical in output but `map` is slightly faster on large inputs because it avoids repeated iterator creation.
- `.a,.b` is two separate outputs, not an array. Wrap in `[]` to get `[.a, .b]`.
- Integer arithmetic in jq uses IEEE 754 doubles. Numbers larger than 2^53 lose precision. Use strings for big integers.

## Related

- [[cheatsheets/jq-syntax]]
- [[cheatsheets/curl-test-patterns]]
- [[cheatsheets/bash-one-liners]]
- [[cheatsheets/find-grep-awk-sed]]
- [[backend/postgres]]
- [[cheatsheets/http-status-codes]]
- [[cheatsheets/index|Cheatsheets]]
