diff --git a/config/m4/check-math-func.m4 b/config/m4/check-math-func.m4 index ce632726ce..5677cedef5 100644 --- a/config/m4/check-math-func.m4 +++ b/config/m4/check-math-func.m4 @@ -1,4 +1,9 @@ -dnl AC_FIND_FUNC(func, arguments) +dnl AC_CHECK_MATH_FUNC(func) AC_DEFUN([AC_CHECK_MATH_FUNC], [ - AC_FIND_FUNC_NO_LIBS([$1], [m], [#include ], [$2]) + AC_LANG(C) + AC_CHECK_LIB([m],[$1],[ + eval "ac_tr_func=HAVE_[]upcase($1)" + AC_DEFINE_UNQUOTED($ac_tr_func) + ],[ + ]) ]) diff --git a/configure.ac b/configure.ac index 9186d00dc5..280694ce6f 100644 --- a/configure.ac +++ b/configure.ac @@ -148,68 +148,68 @@ if test $enable_pthread_tls = yes; then fi dnl libm math.h functions -AC_CHECK_MATH_FUNC(acos, [.5]) -AC_CHECK_MATH_FUNC(acosh, [.5]) -AC_CHECK_MATH_FUNC(asin, [.5]) -AC_CHECK_MATH_FUNC(asinh, [.5]) -AC_CHECK_MATH_FUNC(atan2, [.5,.5]) -AC_CHECK_MATH_FUNC(atan, [.5]) -AC_CHECK_MATH_FUNC(atanh, [.5]) -AC_CHECK_MATH_FUNC(cbrt, [.5]) -AC_CHECK_MATH_FUNC(ceil,[.5]) -AC_CHECK_MATH_FUNC(copysign,[.5,1.0]) -AC_CHECK_MATH_FUNC(cos, [.5]) -AC_CHECK_MATH_FUNC(cosh, [.5]) -AC_CHECK_MATH_FUNC(drem,[.5,1.0]) -AC_CHECK_MATH_FUNC(erf,[.5]) -AC_CHECK_MATH_FUNC(erfc,[.5]) -AC_CHECK_MATH_FUNC(exp10,[.5]) -AC_CHECK_MATH_FUNC(exp2, [.5]) -AC_CHECK_MATH_FUNC(exp, [.5]) -AC_CHECK_MATH_FUNC(expm1,[.5]) -AC_CHECK_MATH_FUNC(fabs,[.5]) -AC_CHECK_MATH_FUNC(fdim,[.5,1.0]) -AC_CHECK_MATH_FUNC(floor, [.5]) -AC_CHECK_MATH_FUNC(fma,[.5,1.0,1.5]) -AC_CHECK_MATH_FUNC(fmax,[.5,1.0]) -AC_CHECK_MATH_FUNC(fmin,[.5,1.0]) -AC_CHECK_MATH_FUNC(fmod,[.5,1.0]) -AC_FIND_FUNC([frexp], [m c], [#include ], [0, 0]) -AC_CHECK_MATH_FUNC(gamma,[.5]) -AC_CHECK_MATH_FUNC(hypot, [.5,.5]) -AC_CHECK_MATH_FUNC(j0, [.5]) -AC_CHECK_MATH_FUNC(j1, [.5]) -AC_CHECK_MATH_FUNC(jn, [1,.5]) -AC_CHECK_MATH_FUNC(ldexp,[.5,2]) -AC_CHECK_MATH_FUNC(lgamma,[.5]) -AC_CHECK_MATH_FUNC(log10, [.5]) -AC_CHECK_MATH_FUNC(log1p,[.5]) -AC_CHECK_MATH_FUNC(log2, [.5]) -AC_CHECK_MATH_FUNC(log, [.5]) -AC_CHECK_MATH_FUNC(logb,[.5]) -AC_CHECK_MATH_FUNC([modf], [m c], [#include ], [0, 0]) -AC_CHECK_MATH_FUNC([lgamma_r], [m c], [#include ], [0, 0]) -AC_CHECK_MATH_FUNC(nearbyint,[.5]) -AC_CHECK_MATH_FUNC(nextafter,[.5,1.0]) -AC_CHECK_MATH_FUNC(nexttoward,[.5,1.0]) -AC_CHECK_MATH_FUNC(pow10,[.5]) -AC_CHECK_MATH_FUNC(pow, [2,2]) -AC_CHECK_MATH_FUNC(remainder, [3,2]) -AC_CHECK_MATH_FUNC(rint,[.5]) -AC_CHECK_MATH_FUNC(round,[.5]) -AC_CHECK_MATH_FUNC(scalb,[.5,1.0]) -AC_CHECK_MATH_FUNC(scalbln,[.5,2]) -AC_CHECK_MATH_FUNC(significand,[.5]) -AC_CHECK_MATH_FUNC(sin, [.5]) -AC_CHECK_MATH_FUNC(sinh, [.5]) -AC_CHECK_MATH_FUNC(sqrt, [.5]) -AC_CHECK_MATH_FUNC(tan, [.5]) -AC_CHECK_MATH_FUNC(tanh, [.5]) -AC_CHECK_MATH_FUNC(tgamma, [.5]) -AC_CHECK_MATH_FUNC(trunc,[.5]) -AC_CHECK_MATH_FUNC(y0, [.5]) -AC_CHECK_MATH_FUNC(y1, [.5]) -AC_CHECK_MATH_FUNC(yn, [1,.5]) +AC_CHECK_MATH_FUNC(acos) +AC_CHECK_MATH_FUNC(acosh) +AC_CHECK_MATH_FUNC(asin) +AC_CHECK_MATH_FUNC(asinh) +AC_CHECK_MATH_FUNC(atan2) +AC_CHECK_MATH_FUNC(atan) +AC_CHECK_MATH_FUNC(atanh) +AC_CHECK_MATH_FUNC(cbrt) +AC_CHECK_MATH_FUNC(ceil) +AC_CHECK_MATH_FUNC(copysign) +AC_CHECK_MATH_FUNC(cos) +AC_CHECK_MATH_FUNC(cosh) +AC_CHECK_MATH_FUNC(drem) +AC_CHECK_MATH_FUNC(erf) +AC_CHECK_MATH_FUNC(erfc) +AC_CHECK_MATH_FUNC(exp10) +AC_CHECK_MATH_FUNC(exp2) +AC_CHECK_MATH_FUNC(exp) +AC_CHECK_MATH_FUNC(expm1) +AC_CHECK_MATH_FUNC(fabs) +AC_CHECK_MATH_FUNC(fdim) +AC_CHECK_MATH_FUNC(floor) +AC_CHECK_MATH_FUNC(fma) +AC_CHECK_MATH_FUNC(fmax) +AC_CHECK_MATH_FUNC(fmin) +AC_CHECK_MATH_FUNC(fmod) +AC_CHECK_MATH_FUNC(frexp) +AC_CHECK_MATH_FUNC(gamma) +AC_CHECK_MATH_FUNC(hypot) +AC_CHECK_MATH_FUNC(j0) +AC_CHECK_MATH_FUNC(j1) +AC_CHECK_MATH_FUNC(jn) +AC_CHECK_MATH_FUNC(ldexp) +AC_CHECK_MATH_FUNC(lgamma) +AC_CHECK_MATH_FUNC(log10) +AC_CHECK_MATH_FUNC(log1p) +AC_CHECK_MATH_FUNC(log2) +AC_CHECK_MATH_FUNC(log) +AC_CHECK_MATH_FUNC(logb) +AC_CHECK_MATH_FUNC(modf) +AC_CHECK_MATH_FUNC(lgamma_r) +AC_CHECK_MATH_FUNC(nearbyint) +AC_CHECK_MATH_FUNC(nextafter) +AC_CHECK_MATH_FUNC(nexttoward) +AC_CHECK_MATH_FUNC(pow10) # Not available with glibc version >= 2.27 +AC_CHECK_MATH_FUNC(pow) +AC_CHECK_MATH_FUNC(remainder) +AC_CHECK_MATH_FUNC(rint) +AC_CHECK_MATH_FUNC(round) +AC_CHECK_MATH_FUNC(scalb) +AC_CHECK_MATH_FUNC(scalbln) +AC_CHECK_MATH_FUNC(significand) +AC_CHECK_MATH_FUNC(sin) +AC_CHECK_MATH_FUNC(sinh) +AC_CHECK_MATH_FUNC(sqrt) +AC_CHECK_MATH_FUNC(tan) +AC_CHECK_MATH_FUNC(tanh) +AC_CHECK_MATH_FUNC(tgamma) +AC_CHECK_MATH_FUNC(trunc) +AC_CHECK_MATH_FUNC(y0) +AC_CHECK_MATH_FUNC(y1) +AC_CHECK_MATH_FUNC(yn) dnl Thread local storage have___thread=no diff --git a/docs/content/3.manual/manual.yml b/docs/content/3.manual/manual.yml index 607d1b4aab..545a9cb11c 100644 --- a/docs/content/3.manual/manual.yml +++ b/docs/content/3.manual/manual.yml @@ -795,6 +795,35 @@ sections: input: '[[0,1], ["a","b","c"]]' output: ['[false, true]'] + - title: "`fetch(key)`, `fetch(key; fallback)`" + body: | + + The builtin function `fetch` returns the value of the path expression of + the input object, or if it is unavailable, an empty object. A path + expression can also be deeply nested. You can also indicate what the + fallback value should be with a second (deeply nested) path expression. + This function was inspired by the function of the same name in Ruby. + + examples: + - program: 'fetch(.foo)' + input: '{"foo": "bar"}' + output: ['"bar"'] + - program: 'fetch(.fu)' + input: '{"foo": "bar"}' + output: [] + - program: 'fetch(.fu; "baz")' + input: '{"foo": "bar"}' + output: ['"baz"'] + - program: 'fetch(.foo.bar)' + input: '{"foo": {"bar": 1}}' + output: ['1'] + - program: 'fetch(.foo.bar; .foo.baz)' + input: '{"foo": {"baz": 2}}' + output: ['2'] + - program: 'fetch(.few; fetch(.fu; .foo))' + input: '{"foo": "bar"}' + output: ['"bar"'] + - title: "`in`" body: | diff --git a/src/builtin.jq b/src/builtin.jq index e65a8997d6..50125e5c96 100644 --- a/src/builtin.jq +++ b/src/builtin.jq @@ -39,6 +39,34 @@ def any(generator; condition): if . then . else empty end)] | length == 1; def any(condition): any(.[]; condition); def any: any(.); +# $key must be an array-path, as with getpath/1 +def fetchpath($key; default): + getpath($key) as $value + | if $value != null then $value + elif ($key|length) == 1 + then if has($key[-1]) then null else default end + else getpath($key[:-1]) as $x + | if $x == null then default + elif $x|has($key[-1]) then null + else default + end + end; +## $key must be an array-path, as with getpath/1 +#def fetchpath($key; default): +# ($key|length) as $len +# | if $len > 0 +# then $key[0] as $k +# | if has($k)? // false +# then . as $in +# | .[$k] | (if $len == 1 then . else fetchpath($key[1:]; $in | default) end) +# else default +# end +# else default +# end; +def fetch(key; default): + fetchpath({} | path(key); default); +def fetchpath(key): fetchpath(key; empty); +def fetch(key): fetch(key; empty); def all(generator; condition): [label $out | foreach generator as $i (true; diff --git a/tests/jq.test b/tests/jq.test index 7e2dd430a2..6050fc0de3 100644 --- a/tests/jq.test +++ b/tests/jq.test @@ -1516,4 +1516,50 @@ isempty(1,error("foo")) null false +fetch(.foo) +{"foo": "bar"} +"bar" +isempty(fetch(.notFoo)) +{"foo": "bar"} +true + +fetch(.foo.baz) +{"fu":0, "foo": {"bar":1, "baz":2}} +2 + +isempty(fetch(.notFoo.baz)) +{} +true + +isempty(fetch(.notFoo.baz)) +{"foo": "baz"} +true + +fetch(.notFoo; "baz") +{"foo": "bar"} +"baz" + +fetch(.notFoo; .fu) +{"foo": "bar", "fu": "baz"} +"baz" + +fetch(.notFoo.bar; .fu) +{"foo": {"bar": true}, "fu": "baz"} +"baz" + +fetch(.notFoo; .fu.baz) +{"foo": "bar", "fu": {"baz": "bor"}} +"bor" + +fetch(.notFoo.bar; .fu.baz) +{"foo": "bar", "fu": {"baz": "bor"}} +"bor" + +fetch(.foo) +{"foo": null} +null + +fetch(.notFoo; fetch(.notFooEither; .foo)) +{"foo": true} +true