Last active
December 8, 2022 22:39
-
-
Save connesc/d6b87cbacae13d4fd58763724049da58 to your computer and use it in GitHub Desktop.
A window function for jq
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Context and benchmarks are available here: https://github.com/stedolan/jq/issues/1577#issuecomment-355756466 | |
def window(values; $size; $step): | |
def checkparam(name; value): if (value | isnormal) and value > 0 and (value | floor) == value then . else error("window \(name) must be a positive integer") end; | |
checkparam("size"; $size) | |
| checkparam("step"; $step) | |
# We need to detect the end of the loop in order to produce the terminal partial group (if any). | |
# For that purpose, we introduce an artificial null sentinel, and wrap the input values into singleton arrays in order to distinguish them. | |
| foreach ((values | [.]), null) as $item ( | |
{index: -1, items: [], ready: false}; | |
(.index + 1) as $index | |
# Extract items that must be reused from the previous iteration | |
| if (.ready | not) then .items | |
elif $step >= $size or $item == null then [] | |
else .items[-($size - $step):] | |
end | |
# Append the current item unless it must be skipped | |
| if ($index % $step) < $size then . + $item | |
else . | |
end | |
| {$index, items: ., ready: (length == $size or ($item == null and length > 0))}; | |
if .ready then .items else empty end | |
); | |
def _nwise($n): window(.[]; $n; $n); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
include "window"; window(empty; 1; 1) | |
null | |
include "window"; window(range(0; 3); 1; 1) | |
null | |
[0] | |
[1] | |
[2] | |
include "window"; window(range(0; 3); 2; 1) | |
null | |
[0, 1] | |
[1, 2] | |
include "window"; window(range(0; 3); 3; 1) | |
null | |
[0, 1, 2] | |
include "window"; window(range(0; 3); 4; 1) | |
null | |
[0, 1, 2] | |
include "window"; window(range(0; 3); 1; 2) | |
null | |
[0] | |
[2] | |
include "window"; window(range(0; 3); 2; 2) | |
null | |
[0, 1] | |
[2] | |
include "window"; window(range(0; 3); 1; 3) | |
null | |
[0] | |
include "window"; window(range(0; 3); 1; 4) | |
null | |
[0] | |
include "window"; window(42; 2; 1) | |
null | |
[42] | |
include "window"; window(4, 2; 2; 1) | |
null | |
[4, 2] | |
include "window"; window(range(0; 10); 3; 2) | |
null | |
[0, 1, 2] | |
[2, 3, 4] | |
[4, 5, 6] | |
[6, 7, 8] | |
[8, 9] | |
include "window"; window(range(0; 10); 3; 3) | |
null | |
[0, 1, 2] | |
[3, 4, 5] | |
[6, 7, 8] | |
[9] | |
include "window"; window(range(0; 10); 3; 4) | |
null | |
[0, 1, 2] | |
[4, 5, 6] | |
[8, 9] | |
include "window"; window(range(0; 10); 3; 5) | |
null | |
[0, 1, 2] | |
[5, 6, 7] | |
include "window"; window(range(0; 10); 3; 7) | |
null | |
[0, 1, 2] | |
[7, 8, 9] | |
include "window"; (0, -1, 1.5, infinite, nan, true, false, null, "1") | try (window(empty; .; 1), false) catch true | |
null | |
true | |
true | |
true | |
true | |
true | |
true | |
true | |
true | |
true | |
include "window"; (0, -1, 1.5, infinite, nan, true, false, null, "1") | try (window(empty; 1; .), false) catch true | |
null | |
true | |
true | |
true | |
true | |
true | |
true | |
true | |
true | |
true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def window(values; $size; $step): | |
def checkparam(name; value): if (value | isnormal) and value > 0 and (value | floor) == value then . else error("window \(name) must be a positive integer") end; | |
checkparam("size"; $size) | |
| checkparam("step"; $step) | |
# Here we are using the finalize form of foreach, which is proposed in #1577. | |
# This does not compile, see "window_finalize_converted" for a working equivalent. | |
| foreach values as $item ( | |
{index: -1, items: [], ready: false}; | |
(.index + 1) as $index | |
# Extract items that must be reused from the previous iteration | |
| if (.ready | not) then .items | |
elif $step >= $size then [] | |
else .items[-($size - $step):] | |
end | |
# Append the current item unless it must be skipped | |
| if ($index % $step) < $size then . + [$item] | |
else . | |
end | |
| {$index, items: ., ready: (length == $size)}; | |
if .ready then .items else empty end; | |
if .ready or (.items | length) == 0 then empty else .items end | |
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def window(values; $size; $step): | |
def checkparam(name; value): if (value | isnormal) and value > 0 and (value | floor) == value then . else error("window \(name) must be a positive integer") end; | |
checkparam("size"; $size) | |
| checkparam("step"; $step) | |
# This is a working equivalent of "window_finalize", which illustrates how the finalize form of foreach can be simulated. | |
| foreach ((values | [.]), null) as $boxed ( | |
{index: -1, items: [], ready: false}; | |
if $boxed == null then . | |
else $boxed[0] as $item | |
| (.index + 1) as $index | |
# Extract items that must be reused from the previous iteration | |
| if (.ready | not) then .items | |
elif $step >= $size then [] | |
else .items[-($size - $step):] | |
end | |
# Append the current item unless it must be skipped | |
| if ($index % $step) < $size then . + [$item] | |
else . | |
end | |
| {$index, items: ., ready: (length == $size)} | |
end; | |
if $boxed == null then | |
if .ready or (.items | length) == 0 then empty | |
else .items | |
end | |
else $boxed[0] as $item | |
| if .ready then .items else empty end | |
end | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment