Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Array into ranges of consecutive numbers [closed]

I am trying to make a Ruby function that converts an array of unique numbers into ranges of consecutive numbers.

[1, 2, 3, 5, 6, 8, 9] => [(1..3), (5..6), (8..9)]

It doesn't seem too hard, but I want to know if there's better way.

like image 979
Sam Kong Avatar asked Sep 18 '13 19:09

Sam Kong


3 Answers

How is this using Enumerable#slice_before?

ar = [1, 2, 3, 5, 6, 8, 9]
prev = ar[0]
p ar.slice_before { |e|
  prev, prev2 = e, prev
  prev2 + 1 != e
}.map{|a| a[0]..a[-1]} 
# >> [1..3, 5..6, 8..9]

ar = [1, 2, 3, 5, 6,7, 8, 9,11]
prev = ar[0]
p ar.slice_before { |e|
  prev, prev2 = e, prev
  prev2 + 1 != e
}.map{|a| a[0]..a[-1]} 
# >> [1..3, 5..9, 11..11]
like image 131
Arup Rakshit Avatar answered Nov 17 '22 07:11

Arup Rakshit


This is something I wrote a while back when dealing with IP address ranges:

class Array
  # [1,2,4,5,6,7,9,13].to_ranges       # => [1..2, 4..7, 9..9, 13..13]
  # [1,2,4,5,6,7,9,13].to_ranges(true) # => [1..2, 4..7, 9, 13]
  def to_ranges(non_ranges_ok=false)
    self.sort.each_with_index.chunk { |x, i| x - i }.map { |diff, pairs|
      if (non_ranges_ok)
        pairs.first[0] == pairs.last[0] ? pairs.first[0] : pairs.first[0] .. pairs.last[0]
      else
        pairs.first[0] .. pairs.last[0]
      end
    }
  end
end

if ($0 == __FILE__)
  require 'awesome_print'

  ary = [1, 2, 4, 5, 6, 7, 9, 13, 12]
  puts ary.join(', ')
  ap ary.to_ranges

  ary = [1, 2, 4, 8, 5, 6, 7, 3, 9, 11, 12, 10]
  puts ary.join(', ')
  ap ary.to_ranges

end

Pass true to to_ranges and it will not convert individual elements into one-element ranges.

like image 39
the Tin Man Avatar answered Nov 17 '22 09:11

the Tin Man


Here's a solution to the same question you're asking. The linked code does a bit more work than you require (the numbers don't need to be sorted, or consecutive), but it'll do the trick. Or, you could use this code, suggested by @NewAlexandria :

class Array
   def to_ranges
      compact.sort.uniq.inject([]) do |r,x|
         r.empty? || r.last.last.succ != x ? r << (x..x) : r[0..-2] << (r.last.first..x)
      end
   end
end
like image 2
Óscar López Avatar answered Nov 17 '22 09:11

Óscar López