diff --git a/dev/snippets.jq b/dev/snippets.jq index e1e29e59..ce4a4ab8 100644 --- a/dev/snippets.jq +++ b/dev/snippets.jq @@ -1,7 +1,3 @@ -# same as group_by but counts -def count_by(exp): - group_by(exp) | map([(.[0] | exp), length]); - def protobuf_to_value: .fields | map({(.name | tostring): (.enum // .value)}) | add; diff --git a/doc/usage.md b/doc/usage.md index 8f2c8d8e..4e7074be 100644 --- a/doc/usage.md +++ b/doc/usage.md @@ -83,6 +83,12 @@ notable is support for arbitrary-precision integers. ### Functions - All standard library functions from jq + - `chunk/1`, `chunk_by/1` like `group` but groups consecutively on condition. + - `count_by` like `group_by` but counts groups lengths. + - `debug/1` like `debug/0` but uses arg to produce debug message. `{a: 123} | debug({a}) | ...`. + - `path_to_expr` from `["key", 1]` to `".key[1]"`. + - `expr_to_path` from `".key[1]"` to `["key", 1]`. + - `diff/2` produce diff object between two values. - `open` open file for reading - `probe` or `decode` try to automatically detect format and decode - `mp3`, `matroska`, ..., ``, `decode([name])` try decode as format diff --git a/pkg/interp/funcs.jq b/pkg/interp/funcs.jq index d4813d9e..cf3948cf 100644 --- a/pkg/interp/funcs.jq +++ b/pkg/interp/funcs.jq @@ -45,6 +45,43 @@ def trim: capture("^\\s*(?.*?)\\s*$"; "").str; # does +1 and [:1] as " "*0 is null def rpad($s; $w): . + ($s * ($w+1-length))[1:]; +# like `group` but groups consecutively on condition +def chunk_by(f): + ( . as $a + | length as $l + | if $l == 0 then [] + else + ( [ foreach $a[] as $v ( + {cf: ($a[0] | f), index: 0, start: 0, extract: null}; + ( ($v | f) as $vf + | (.index == 0 or (.cf == $vf)) as $equal + | if $equal then + ( .extract = null + ) + else + ( .cf = $vf + | .extract = [.start, .index] + | .start = .index + ) + end + | .index += 1 + ); + ( if .extract then .extract else empty end + , if .index == $l then [.start, .index] else empty end + ) + ) + ] + | map($a[.[0]:.[1]]) + ) + end + ); +# [1, 2, 2, 3] => [[1], [2, 2], [3] +def chunk: chunk_by(.); + +# same as group_by but counts +def count_by(exp): + group_by(exp) | map([(.[0] | exp), length]); + # helper to build path query/generate functions for tree structures with # non-unique children, ex: mp4_path def tree_path(children; name; $v): diff --git a/pkg/interp/funcs_test.jq b/pkg/interp/funcs_test.jq index 617b58f6..57cd5459 100644 --- a/pkg/interp/funcs_test.jq +++ b/pkg/interp/funcs_test.jq @@ -1,14 +1,29 @@ include "assert"; include "funcs"; -[ - ".", - ".a", - ".a[0]", - ".a[123].bb", - ".[123].a", - ".[123][123].a", - ".\"b b\"", - ".\"a \\\\ b\"", - ".\"a \\\" b\"" -][] | assert("\(.) | expr_to_path | path_to_expr"; .; expr_to_path | path_to_expr) +( + ([ + ".", + ".a", + ".a[0]", + ".a[123].bb", + ".[123].a", + ".[123][123].a", + ".\"b b\"", + ".\"a \\\\ b\"", + ".\"a \\\" b\"" + ][] | assert("\(.) | expr_to_path | path_to_expr"; .; expr_to_path | path_to_expr)) +, + ([ + [[], []], + [[1], [[1]]], + [[1,1], [[1,1]]], + [[1,1,2], [[1,1],[2]]], + [[1,1,2,2], [[1,1],[2,2]]], + [[1,2,2,1], [[1],[2,2],[1]]] + ][] | assert("\(.) | chunk"; .[1]; .[0] | chunk)) +, + ([ + [[{a:1},{a:1},{a:2}], [[{a:1},{a:1}],[{a:2}]]] + ][] | assert("\(.) | chunk_by"; .[1]; .[0] | chunk_by(.a))) +) diff --git a/pkg/interp/internal.jq b/pkg/interp/internal.jq index 0bdbb025..041c045f 100644 --- a/pkg/interp/internal.jq +++ b/pkg/interp/internal.jq @@ -4,6 +4,7 @@ def debug: ( ((["DEBUG", .] | tojson), "\n" | stderr) , . ); + def debug(f): . as $c | f | debug | $c; # eval f and finally eval fin even on empty or error def finally(f; fin):