How do I summarize array of integers as an array of ranges?

耗尽温柔 提交于 2019-11-29 02:05:45

Functional approach using 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]]

How it works: once indexed, consecutive elements in the array have the same x - idx, so we use that value to chunk (grouping of consecutive items) the input array. Finally we just need to take the first and last elements of each group to build the pairs.

This is almost straight from the enumerable#slice_before method documentation:

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]

This should work with characters, dates, anything with a .succ method.

Marc-André Lafortune

An even easier solution than @tokland's very nice one is using chunk_while:

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

Note: chunk_while was introduced in Ruby 2.3

DigitalRoss

Hmm, well, it's not tokland's masterpiece, but I think it may be a good straightforward solution...

[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

Another approach

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

Similar to Larsenal's method but using inject to manage the boring stuff.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!