How to sum an array of integers as an array of ranges?

I would like to enter data such as:

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

and turn it into something like the following:

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

Each auxiliary array represents a range of integers.

+7
source share
5 answers

Functional approach using Enumerated # chunk :

 xs.enum_for(:chunk).with_index { |x, idx| x - idx }.map do |diff, group| [group.first, group.last] end # => [[1, 2], [4, 7], [9, 9], [13, 13]] 

How it works: after indexing, consecutive elements in the array have the same x - idx , so we use this value to group (group consecutive elements) the input array. Finally, we just need to take the first and last elements of each group to create pairs.

+19
source

This is almost straight from the # slice_before enumerated method :

 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, using the .succ method.

+4
source

Hmm, well, this is not a masterpiece, but I think it might be a good direct 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 
+3
source

Even simpler than @tokland uses chunk_while very nicely :

 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

+2
source

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 injections to control boring material.

0
source

All Articles