jq Filter Tester

Run a jq filter against a JSON document in your browser. Put the filter on a line beginning filter: and the JSON below it. Anything outside the supported subset is reported rather than silently ignored.

Paste below, or drop a file anywhere on this panel

Or drop a file anywhere on this panel. Nothing is uploaded: the analysis runs in this tab.

The answer appears here

Paste on the left and press Run the filter. Nothing leaves this tab.

Examples

Real input you can load into the tool above. Each one shows a different thing going wrong, because that is what the tool is for.

A stream, not an array

Three separate results, printed one per line. Every downstream filter runs once per result, which is why piping into a function applies it per item.

filter: .items[] | .name
{
    "items": [
    { "name": "alpha", "active": true },
    { "name": "beta", "active": false },
    { "name": "gamma", "active": true }
  ]
}

select matching nothing

An empty stream rather than an error. A filter that matches nothing and a filter that is wrong produce identical output, which is why an empty result is ambiguous.

filter: .items[] | select(.qty > 1000)
{
    "items": [
    { "name": "alpha", "qty": 3 },
    { "name": "beta", "qty": 10 }
  ]
}

A misspelled key is null, not an error

A path that does not exist evaluates to null and a chain through it stays null. Nothing distinguishes a typo from a genuinely absent value.

filter: .meta.regoin
{
    "meta": { "region": "eu-west-1" }
}

Real jq that this tester does not implement

reduce is a jq construct outside the supported subset. It is reported by name rather than ignored, because a playground that silently skips a construct gives an answer that looks right and is not.

filter: reduce .items[] as $x (0; . + $x.qty)
{
    "items": [ { "qty": 3 }, { "qty": 10 } ]
}

Common mistakes

These are the ones that fail silently. The config is accepted, nothing raises an error, and the consequence arrives later.

  1. Expecting `.[]` to give you an array

    A jq filter emits a STREAM. `.items[] | .name` produces three separate results, and every downstream filter runs once per result rather than once on the collection.

    Instead:Wrap the whole filter in `[ ... ]` to collect the stream into one array.

  2. Reading an empty output as a broken filter

    `select()` emits nothing when its condition is false, and that is not an error. A filter that matches nothing and a filter that is wrong produce identical output.

    Instead:Build it left to right, and wrap in `[ ... ]` so an empty result shows as `[]` rather than as nothing.

  3. Trusting that a path with no error is a path that exists

    `.foo` on an object without `foo` is `null`, and a chain through it stays `null`. A misspelled key and a genuinely absent value are indistinguishable.

    Instead:Use `has("key")` to tell them apart, and `keys` to see what the object actually contains.

  4. Assuming a filter that works on one document works on the next

    `.foo` on `null` is fine and on a number is fatal, so the same filter is silent on missing data and errors on data of the wrong shape.

    Instead:Add `?` for an optional access, or filter by type first: `.items[] | objects | .name`.

  5. Building an object from a stream and expecting one object

    Object construction is a cartesian product over its values. `{n: .items[].name}` produces one object per name, not one object holding all of them.

    Instead:Collect first: `{names: [.items[].name]}`.

A filter produces a stream, not a value

Almost every jq surprise comes from one design decision: a filter emits zero, one or many results, and everything downstream runs once per result. Once that lands, the rest of the language stops being strange.

That is why .[] gives you lines rather than an array

`.items[] | .name` emits three separate results, printed one per line. It is not an array and piping it into a function applies that function to each item rather than to the collection. Wrapping the whole filter in square brackets collects the stream back into one array, which is almost always what a program consuming the output needs.

.items[] | .name        "alpha"
                        "beta"
                        "gamma"     three results

[.items[] | .name]      ["alpha","beta","gamma"]   one result

select emits nothing rather than false

When the condition is false it produces zero results, not `false` and not `null`. That is what makes it compose, and it is also why an empty output is ambiguous: a filter that matches nothing and a filter that is wrong look identical. Test the pieces left to right, and wrap in brackets so an empty result shows as `[]` rather than as nothing at all.

A missing key is null, and a wrong type is fatal

`.foo` on an object without `foo` is `null`, and a chain through it stays `null` all the way down, so a misspelled path produces no error at all. But `.foo` on a NUMBER is an error. The same filter is therefore silent on missing data and fatal on data of the wrong shape, which is exactly why a filter that worked in testing fails on the third document in production.

{} | .a.b.c          null,   no error
5  | .a              error:  cannot index number
5  | .a?             nothing, the ? makes it optional
.items[] | objects | .name    skips anything not an object

Object construction multiplies over streams

`{n: .items[].name}` does not build one object with three names. Each value is a stream, so the result is the cartesian product: three objects with one name each. It is consistent with everything above and it catches everyone once.

What this cannot do, and it will tell you

jq is a complete language with variables, reduce, foreach, if, try and user-defined functions. This implements a documented subset, and anything outside it is reported as an error naming what was not understood rather than being quietly ignored. That distinction is the whole point: a playground that skips a construct gives you an answer that looks like jq's and is not. Supported: ., .foo, .foo.bar, ."odd key", .foo?, .[], .[]?, .[0], .[1:3], |, ,, [ ... ], { a: .b }, select(f), map(f), has("k"), keys, keys_unsorted, values, length, type, not, to_entries, from_entries, add, unique, sort, sort_by(f), reverse, min, max, flatten, first, last, empty, tostring, tonumber, ascii_downcase, ascii_upcase, startswith(s), endswith(s), contains(s), join(s), split(s), ==, !=, <, <=, >, >=, and, or, +, -, *, /, %, numbers, strings, booleans, arrays, objects, nulls. Everything here was checked against real jq, 82 filters over one document with complete agreement.