I want to combine consecutive integers in a sorted array and replace them with ranges using jq.
input:
[1,2,3,4,6,98,99,101]
desired output:"1-4,6,98-99,101"
input:
[1,3,5]
desired output:"1,3,5"
input:
[1,2,3,4,5]
desired output:"1-5"
I have found a solution using foreach, but it does not seem very elegant and compact to me.
Is there a simpler solution for this task?
[foreach (.[], 99999) as $current
({};
if length == 0 then
{first: $current}
elif (has("last") | not) and .first + 1 != $current then
{first: $current, extract: "\(.first)"}
elif has("last") and .last + 1 != $current then
{first: $current, extract: "\(.first)-\(.last)"}
else
{first, last: $current}
end;
.extract // empty
)]
| join(",")
I'd do it in two steps.
First, group into ranges.
reduce .[] as $_ (
[];
if $_ - 1 == .[-1][1] then
.[-1][1] = $_
else
. + [ [ $_, $_ ] ]
end
)
This step produces [[1,1],[2,4],[6,6],[98,99],[101,101]].
Then, build the string.
map(
if .[0] == .[1] then
"\( .[0] )"
else
"\( .[0] )-\( .[1] )"
end
) |
join(",")
Demo on jqplay
It's not much shorter, but I think it's a lot simpler and cleaner.
Also, separating the concerns doesn't just have beneficial consequences for simplicity and clarity. You gain the option of hiding the code in two small functions. And you can customize the output more easily, say to produce 98,99 instead of 98-99 for [98,99]. You could even decide to reuse the code where you don't need to produce a string at all.
But best of all, this solution eliminates the magic 99999!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With