jq

Determine if any value exists matching a filter expression while treating an explicit null as existing


I'm trying to determine whether a jq expression refers to no items at all, without collapsing null and empty.

As I understand it, .foo? should be identical to try .foo catch empty. Because catch empty is different from catch null, I would then expect the following to return true with <<<'{}' but false with <<<'{"foo": {"bar": null}}':

$ jq 'isempty(.foo?.bar?)' <<<'{}'
false
$ jq 'isempty(.foo?.bar?)' <<<'{"foo": {"bar": null}}'
false

...as the result is identical between both cases, clearly my expectation is incorrect.


The goal is to find something I can use in place of isempty above, where the result will be distinguishable between inputs that do and don't have any value matching the expression, even when the value that exists is false or null, working for arbitrary paths (which may or may not walk arrays, etc).


Solution

  • Since isempty is already defined in builtin.jq, I'll propose the following def:

    def hasValue(g):
      del(g) != . ;
    

    With this we see:

    ({},
     {"foo": {"bar": null}})
    | hasValue(.foo?.bar?)
    

    evaluates to:

    false
    true
    

    If you want errors to be caught, you might also wish to consider:

    def hasValue(g):
      try (del(g) != .) catch false;
    

    Does that help?