用范围替换数组中的连续整数

Replace consecutive integers in an array with ranges

我想将连续的整数合并到一个排序数组中,并使用 jq 将它们替换为范围。

示例 1

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

示例 2

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

示例 3

input: [1,2,3,4,5]
desired output: "1-5"

我找到了一个使用 foreach 的解决方案,但它对我来说似乎不是很优雅和紧凑。

这个任务有更简单的解决方案吗?

[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(",")

我会分两步完成。


首先,分组到范围内。

reduce .[] as $_ (
   [];
   if $_ - 1 == .[-1][1] then
      .[-1][1] = $_
   else
      . + [ [ $_, $_ ] ]
   end
)

此步骤生成 [[1,1],[2,4],[6,6],[98,99],[101,101]]


然后,构建字符串。

map(
   if .[0] == .[1] then
      "\( .[0] )"
   else
      "\( .[0] )-\( .[1] )"
   end
) |
join(",")

Demo 在 jqplay

它并没有短多少,但我认为它更简单、更清晰。

此外,分离关注点不仅对简单性和清晰性有好处。您可以选择将代码隐藏在两个小函数中。您可以更轻松地自定义输出,比如为 [98,99] 生成 98,99 而不是 98-99。您甚至可以决定重用根本不需要生成字符串的代码。

但最重要的是,此解决方案消除了魔法 99999

这是另一个解决方案:

reduce .[1:][] as $n ([.[:1]];
  if $n == .[-1][-1] + 1 then .[-1][1] = $n else . + [[$n]] end
) | map(join("-")) | join(",")

喂食时

[1,2,3,4,6,98,99,101]
[1,3,5]
[1,2,3,4,5]
[4]
[]

它输出

"1-4,6,98-99,101"
"1,3,5"
"1-5"
"4"
""

Demo