Filtering

Filter an Array of Objects by a Nested Key (jq)

3 min · updated June 14, 2026

Real JSON nests. Here’s how to filter on a value that lives a level (or two) down, or inside an inner array.

Example input

[
  { "name": "Ada",   "address": { "city": "London" }, "tags": ["dev","math"] },
  { "name": "Linus", "address": { "city": "Portland" }, "tags": ["kernel"] },
  { "name": "Mae",   "address": { "city": "London" }, "tags": ["dev","space"] }
]

Filter by a nested object field

jq '[.[] | select(.address.city == "London")]' data.json

Output:

[
  { "name": "Ada", "address": { "city": "London" }, "tags": ["dev","math"] },
  { "name": "Mae", "address": { "city": "London" }, "tags": ["dev","space"] }
]

Filter where an inner array CONTAINS a value

jq '[.[] | select(.tags | index("dev"))]' data.json

index("dev") returns the position (truthy) if present, null if not — so select keeps the matches. IN / any work too:

jq '[.[] | select(.tags | any(. == "dev"))]' data.json

Filter where ANY nested item matches a condition

{ "orders": [ { "items": [ {"sku":"A","qty":5}, {"sku":"B","qty":1} ] } ] }
jq '.orders[] | select(.items | any(.qty > 3))' nested.json

Safe when the nested key may be missing

Use ? so absent paths don’t error:

jq '[.[] | select(.address.city? == "London")]' data.json

any(condition) is the workhorse for “at least one sub-item matches”; all(condition) for “every sub-item matches”. Both take the array on their left via a pipe: .tags | any(. == "dev").

← All recipes