def halt_error: halt_error(5); def error(msg): msg|error; def select(f): if f then . else empty end; def map(f): [.[] | f]; def map_values(f): .[] |= f; def sort_by(f): _sort_by_impl(map([f])); def group_by(f): _group_by_impl(map([f])); def unique_by(f): _unique_by_impl(map([f])); def max_by(f): _max_by_impl(map([f])); def min_by(f): _min_by_impl(map([f])); def to_entries: [keys_unsorted[] as $k | {key: $k, value: .[$k]}]; def from_entries: map({ (.key // .Key // .name // .Name): if has("value") then .value else .Value end }) | add // {}; def with_entries(f): to_entries | map(f) | from_entries; def indices($i): if type == "array" and ($i|type) == "array" then .[$i] elif type == "array" then .[[$i]] elif type == "string" and ($i|type) == "string" then _strindices($i) else .[$i] end; def index($i): indices($i) | .[0]; # TODO: optimize def rindex($i): indices($i) | .[-1:][0]; # TODO: optimize def in(xs): . as $x | xs | has($x); def inside(xs): . as $x | xs | contains($x); def join($x): reduce .[] as $i (null; (if .==null then "" else .+$x end) + ($i | if type=="boolean" or type=="number" then tostring else .//"" end) ) // ""; def ltrimstr($left): if startswith($left) then .[$left | length:] end; def rtrimstr($right): if endswith($right) then .[:length - ($right | length)] end; def trimstr($val): ltrimstr($val) | rtrimstr($val); def match(re; mode): _match_impl(re; mode; false)|.[]; def test(re; mode): _match_impl(re; mode; true); def capture(re; mods): match(re; mods) | reduce ( .captures | .[] | select(.name != null) | { (.name) : .string } ) as $pair ({}; . + $pair); def scan($re; $flags): match($re; "g" + $flags) | if (.captures|length > 0) then [ .captures | .[] | .string ] else .string end; def fromdateiso8601: strptime("%Y-%m-%dT%H:%M:%SZ")|mktime; def todateiso8601: strftime("%Y-%m-%dT%H:%M:%SZ"); def fromdate: fromdateiso8601; def todate: todateiso8601; def inputs: try repeat(input) catch if .=="break" then empty else error end; def debug(msgs): (msgs | debug | empty), .; def while(cond; update): def _while: if cond then ., (update | _while) else empty end; _while; def until(cond; next): def _until: if cond then . else (next|_until) end; _until; def limit($n; expr): if $n > 0 then label $out | foreach expr as $item ($n; . - 1; $item, if . <= 0 then break $out else empty end) elif $n == 0 then empty else error("limit doesn't support negative count") end; def skip($n; expr): if $n > 0 then foreach expr as $item ($n; . - 1; if . < 0 then $item else empty end) elif $n == 0 then expr else error("skip doesn't support negative count") end; def combinations: if length == 0 then [] else .[0][] as $x | (.[1:] | combinations) as $y | [$x] + $y end; def combinations(n): . as $dot | [range(n) | $dot] | combinations; def IN(s): any(s == .; .); def IN(src; s): any(src == s; .); def abs: if . < 0 then - . else . end; def isfinite: type == "number" and (isinfinite | not); def arrays: select(type == "array"); def objects: select(type == "object"); def iterables: select(type|. == "array" or . == "object"); def booleans: select(type == "boolean"); def numbers: select(type == "number"); def normals: select(isnormal); def finites: select(isfinite); def strings: select(type == "string"); def nulls: select(. == null); def values: select(. != null); def scalars: select(type|. != "array" and . != "object"); def first: .[0]; def last: .[-1]; def nth($n): .[$n]; def reverse: [.[length - 1 - range(0;length)]]; def flatten($x): if $x < 0 then error("flatten depth must not be negative") else _flatten($x) end; def flatten: _flatten(-1); def recurse(f): def r: ., (f | r); r; def recurse(f; cond): def r: ., (f | select(cond) | r); r; def recurse: recurse(.[]?); def repeat(exp): def _repeat: exp, _repeat; _repeat; def _modify(paths; update): reduce path(paths) as $p ([., []]; . as $dot | null | label $out | ($dot[0] | getpath($p)) as $v | ( ( $$$$v | update | (., break $out) as $v def add(f): reduce f as $x (null; . + $x); def add: add(.[]); def all(generator; condition): isempty(generator|condition and empty); def any(generator; condition): isempty(generator|condition or empty)|not; def _flatten($x): reduce .[] as $i ([]; if $i | type == "array" and $x != 0 then . + ($i | _flatten($x-1)) else . + [$i] end); def paths: path(recurse)|select(length > 0); def paths(node_filter): path(recurse|select(node_filter))|select(length > 0); def pick(pathexps): . as $in | reduce path(pathexps) as $a (null; setpath($a; $in|getpath($a)) ); def _assign(paths; $value): reduce path(paths) as $p (.; setpath($p; $value)); def _modify(paths; update): reduce path(paths) as $p ([., []]; . as $dot | ($dot[0] | getpath($p)) as $v | $$$$dot | setpath([0] + $p; $v) def del(f): delpaths([path(f)]); # ensure the output of debug(m1,m2) is kept together: def debug(msgs): (msgs | debug | empty), .;