Filtering a list down to what you care about, like failed jobs or large files, is the core of most jq one-liners.
select(cond) outputs its input if the condition is true, and nothing otherwise. Used after .[], it filters a stream:
.[] | select(.status == "failed")
Conditions use ==, !=, <, <=, >, >=, combined with and, or and not (which is a filter, written cond | not). In jq only false and null are falsy; 0, "" and [] count as true.
map(f) applies f to every element of an array and returns a new array. It is exactly [.[] | f]. So these two are equivalent ways to filter an array while keeping it an array:
map(select(.price > 100))[.[] | select(.price > 100)]
Useful tests inside select: has("key"), .tags | index("urgent"), .name | startswith("tmp"), test("regex"), and IN(...) for membership: select(.env | IN("prod","staging")).
map_values(f) does the same for an object's values. Note that select drops non-matching inputs entirely, so map(select(...)) can shorten an array, while map(if ... then ... else . end) keeps its length.
Example
cat > jobs.json <<'EOF'
[{"id":1,"status":"ok","ms":120},{"id":2,"status":"failed","ms":900},
{"id":3,"status":"failed","ms":40},{"id":4,"status":"ok","ms":3000}]
EOF
jq -c '.[] | select(.status == "failed") | .id' jobs.json # 2 3
jq -c 'map(select(.ms > 500) | .id)' jobs.json # [2,4]
jq -c 'map(select(.status == "ok" and .ms < 1000))' jobs.json
# [{"id":1,"status":"ok","ms":120}]
jq -c 'map(.ms * 2)' jobs.json # [240,1800,80,6000]
jq -c '[.[] | select(.id | IN(1,4)) | .id]' jobs.json # [1,4]
jq 'map(select(.status != "ok")) | length' jobs.json # 2Remember
- select(cond) passes input through only when cond is true
- map(f) = [.[] | f]; map(select(...)) filters an array
- Only false and null are falsy in jq
Watch out for
- Using select on an array directly (select(.x) on [...]) instead of on its elements; iterate or map first.