diff --git a/docs/content/3.manual/manual.yml b/docs/content/3.manual/manual.yml index b9b8a23af5..602a70c2e3 100644 --- a/docs/content/3.manual/manual.yml +++ b/docs/content/3.manual/manual.yml @@ -892,6 +892,18 @@ sections: input: '[[],{},1,"foo",null,true,false]' output: ['1'] + - title: "`isarray`, `isobject`, `isboolean`, `isnumber`, `isnormal`, `isfinite`, `isstring`, `isnull`, `isvalue`, `isscalar`" + body: | + + These built-ins produce whether inputs are arrays, objects, + booleans, numbers, normal numbers, finite numbers, strings, + null, non-null values, and non-iterables, respectively. + + examples: + - program: '.[]|isnumber' + input: '[[],{},1,"foo",null,true,false]' + output: ['false', 'false', 'true', 'false', 'false', 'false', 'false'] + - title: "`empty`" body: | @@ -937,7 +949,7 @@ sections: That is, `paths(numbers)` outputs the paths to all numeric values. - `leaf_paths` is an alias of `paths(scalars)`; `leaf_paths` is + `leaf_paths` is an alias of `paths(isscalar)`; `leaf_paths` is *deprecated* and will be removed in the next major release. examples: diff --git a/src/builtin.jq b/src/builtin.jq index 53998a859c..5c430f8f35 100644 --- a/src/builtin.jq +++ b/src/builtin.jq @@ -29,7 +29,8 @@ def indices($i): if type == "array" and ($i|type) == "array" then .[$i] else .[$i] end; def index($i): indices($i) | .[0]; # TODO: optimize def rindex($i): indices($i) | .[-1:][0]; # TODO: optimize -def paths: path(recurse(if (type|. == "array" or . == "object") then .[] else empty end))|select(length > 0); +def paths: def recurse_pre(f; cond): def r: ., (select(cond) | f | r); r; + path(recurse_pre(.[]?;.!=null))|select(length > 0); def paths(node_filter): . as $dot|paths|select(. as $p|$dot|getpath($p)|node_filter); def any(generator; condition): [label $out | foreach generator as $i @@ -46,19 +47,27 @@ def all(generator; condition): def all(condition): all(.[]; condition); def all: all(.); def isfinite: type == "number" and (isinfinite | not); -def arrays: select(type == "array"); -def objects: select(type == "object"); +def isarray: type == "array"; +def arrays: select(isarray); +def isobject: type == "object"; +def objects: select(isobject); def iterables: arrays, objects; -def booleans: select(type == "boolean"); -def numbers: select(type == "number"); +def isboolean: type == "boolean"; +def booleans: select(isboolean); +def isnumber: type == "number"; +def numbers: select(isnumber); def normals: select(isnormal); def finites: select(isfinite); -def strings: select(type == "string"); -def nulls: select(type == "null"); -def values: select(. != null); -def scalars: select(. == null or . == true or . == false or type == "number" or type == "string"); +def isstring: type == "string"; +def strings: select(isstring); +def isnull: type == "null"; +def nulls: select(isnull); +def isvalue: . != null; +def values: select(isvalue); +def isscalar: . == null or . == true or . == false or type == "number" or type == "string"; +def scalars: select(isscalar); def scalars_or_empty: select(. == null or . == true or . == false or type == "number" or type == "string" or ((type=="array" or type=="object") and length==0)); -def leaf_paths: paths(scalars); +def leaf_paths: paths(isscalar); def join($x): reduce .[] as $i (null; (if .==null then "" else .+$x end) + ($i | if type=="boolean" or type=="number" then tostring else .//"" end) diff --git a/tests/jq.test b/tests/jq.test index 630c344d0f..420f8ea354 100644 --- a/tests/jq.test +++ b/tests/jq.test @@ -743,10 +743,26 @@ path(.a[path(.b)[0]]) [1,[[],{"a":2}]] [[0],[1],[1,0],[1,1],[1,1,"a"]] +# Make sure we get paths to nulls +[paths] +[1,null] +[[0],[1]] + +# Make sure we don't get paths to lone items +[paths] +5 +[] + [leaf_paths] [1,[[],{"a":2}]] [[0],[1,1,"a"]] +# Verifying that leaf_paths picks up false leaves. +# Issue #1163 +[leaf_paths] +{ "a": 2, "b": false, "d": [] } +[ [ "a" ], [ "b" ] ] + ["foo",1] as $p | getpath($p), setpath($p; 20), delpaths([$p]) {"bar": 42, "foo": ["a", "b", "c", "d"]} "b" @@ -1134,10 +1150,18 @@ map([1,2][0:.]) [1,2,"foo",[],[3,[]],{},true,false,null] [[],[3,[]]] +[.[]|isarray] +[1,2,"foo",[],[3,[]],{},true,false,null] +[false,false,false,true,true,false,false,false,false] + [.[]|objects] [1,2,"foo",[],[3,[]],{},true,false,null] [{}] +[.[]|isobject] +[1,2,"foo",[],[3,[]],{},true,false,null] +[false,false,false,false,false,true,false,false,false] + [.[]|iterables] [1,2,"foo",[],[3,[]],{},true,false,null] [[],[3,[]],{}] @@ -1146,18 +1170,34 @@ map([1,2][0:.]) [1,2,"foo",[],[3,[]],{},true,false,null] [1,2,"foo",true,false,null] +[.[]|isscalar] +[1,2,"foo",[],[3,[]],{},true,false,null] +[true,true,true,false,false,false,true,true,true] + [.[]|values] [1,2,"foo",[],[3,[]],{},true,false,null] [1,2,"foo",[],[3,[]],{},true,false] +[.[]|isvalue] +[1,2,"foo",[],[3,[]],{},true,false,null] +[true,true,true,true,true,true,true,true,false] + [.[]|booleans] [1,2,"foo",[],[3,[]],{},true,false,null] [true,false] +[.[]|isboolean] +[1,2,"foo",[],[3,[]],{},true,false,null] +[false,false,false,false,false,false,true,true,false] + [.[]|nulls] [1,2,"foo",[],[3,[]],{},true,false,null] [null] +[.[]|isnull] +[1,2,"foo",[],[3,[]],{},true,false,null] +[false,false,false,false,false,false,false,false,true] + flatten [0, [1], [[2]], [[[3]]]] [0, 1, 2, 3]