Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replace consecutive integers in an array with ranges

Tags:

jq

I want to combine consecutive integers in a sorted array and replace them with ranges using jq.

example 1

input: [1,2,3,4,6,98,99,101]
desired output: "1-4,6,98-99,101"

example 2

input: [1,3,5]
desired output: "1,3,5"

example 3

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(",")
like image 765
jpseng Avatar asked Feb 14 '26 20:02

jpseng


1 Answers

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!

like image 52
ikegami Avatar answered Feb 16 '26 18:02

ikegami



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!