如何将整数数组汇总为范围数组?

时间:2011-12-24 00:08:22

标签: ruby arrays algorithm functional-programming range

我想接受输入,例如:

[1,2,4,5,6,7,9,13]

并将其转换为以下内容:

[[1,2],[4,7],[9,9],[13,13]]

每个子数组代表一个整数范围。

5 个答案:

答案 0 :(得分:21)

使用Enumerable#chunk的功能方法:

ranges = [1, 2, 4, 5, 6, 7, 9, 13]
  .enum_for(:chunk) # .chunk for Ruby >= 2.4
  .with_index { |x, idx| x - idx }
  .map { |_diff, group| [group.first, group.last] }

#=> [[1, 2], [4, 7], [9, 9], [13, 13]]

工作原理:一旦索引,数组中的连续元素具有相同的x - idx,因此我们使用该值来对输入数组进行块(连续项的分组)。最后,我们只需要采用每个组的第一个和最后一个元素来构建对。

答案 1 :(得分:5)

这几乎直接来自enumerable#slice_before方法文档:

ar = [1,2,4,5,6,7,9,13]
prev = ar[0]
ar.slice_before{|e|prev,prev2 = e,prev; prev2.succ != e}.map{|a|a.first..a.last}
#=> [1..2, 4..7, 9..9, 13..13]

这应该适用于使用.succ方法的字符,日期和任何内容。

答案 2 :(得分:4)

@tokland's very nice one使用chunk_while更简单的解决方案:

xs.chunk_while { |a, b| a + 1 == b }.map do |seq|
  [seq.first, seq.last]
end

注意:在Ruby 2.3中引入了chunk_while

答案 3 :(得分:3)

嗯,好吧,这不是tokland's杰作,但我认为这可能是一个很好的直接解决方案......

[1,2,4,5,6,7,9,13].inject([]) do |m, v|
  if m.last.to_a.last == v.pred
    m[-1][-1] = v
  else
    m << [v, v]
  end
  m
end

答案 4 :(得分:0)

另一种方法

def summarize(x)
  x.inject([]) do |acc, value|
    if acc.last && acc.last[1] + 1 == value
      acc.last[1] = value
      acc
    else
      acc << [value,value]
    end
  end
end

与Larsenal的方法类似,但使用注入来管理无聊的东西。