将数字数组转换为范围

时间:2013-12-30 20:42:36

标签: ruby ruby-on-rails-4

我希望将带有间隙的数字数组转换为Ruby中多个范围的数组。每个范围应确定序列中的间隙。假设你有阵列:

[1,2,3,5,6,8,9,10,11,12]

预期结果为:[1-3,5-6,8-12]

我无法提出解决问题的好主意。我怎样才能解决这个问题?

5 个答案:

答案 0 :(得分:7)

我会使用Enumerable#slice_before执行以下操作:

a = [1,2,3,5,6,8,9,10,11,12]
prev = a[0]
p a.slice_before { |e|
  prev, prev2 = e, prev
  prev2 + 1 != e
}.map{|b,*,c| c ? (b..c) : b }
# >> [1..3, 5..6, 8..12]

答案 1 :(得分:1)

a = [1,2,3,5,8,9,10,11,12]

b = [a.first-1] + (a.first..a.last).to_a - a + [a.last + 1]
  # => [0, 4, 6, 7, 13]
b.each_cons(2).with_object([]) {|(i,j), c| c << (i+1..j-1) if j > i+1}
  # => [1..3, 5..5, 8..12] 

可替换地,

b = [a.first] + ((a.first..a.last).to_a - a).flat_map {|e| [e-1,e+1]}+[a.last]
  # => [1, 3, 5, 5, 7, 6, 8, 12]
b.each_slice(2).map {|f,l| l >= f ? f..l : nil}.compact
  # => [1..3, 5..5, 8..12]

注意:b.each_slice(2).to_a # => [[1, 3], [5, 5], [7, 6], [8, 12]]

答案 2 :(得分:0)

另一种解决方案:

class Array
  def to_range_array
    res = [ Range.new(first,first) ]
    self[1..-1].sort.each{|item|
      if res.last.max == (item -1)
        res << Range.new(res.pop.min, item)
      else
        res << Range.new(item, item)
      end
    }
    res
  end
end

array =  [ 1,2,3,5,6,8,9,10,11,12 ]
p array.to_range_array #[1..3, 5..6, 8..12]

我的假设:

  • 范围基于已排序的数组。 [ 9,10,11,12, 1,2,3,5,6,8]应返回与[ 1,2,3,5,6,8,9,10,11,12 ]相同的结果。 如果没有sort-command,您将获得[9..12, 1..3, 5..6, 8..8]
  • 单个值会产生一个值范围(例如8..8

答案 3 :(得分:0)

a = [1,2,3,5,6,8,9,10,11,12]

b = ((a.first..a.last+1).to_a - a).unshift(-(2**(0.size * 8 -2)))
  # => [-4611686018427387904, 4, 7, 13]
c = a.slice_before {|i| i > b.first ? b.shift : false}.to_a
  # => [[1, 2, 3], [5, 6], [8, 9, 10, 11, 12]]
c.map {|e| (e.first..e.last)}
  # => [1..3, 5..6, 8..12] 

答案 4 :(得分:0)

我更喜欢使用Enumerable#slice_before的所有变体,但这是使用Enumerable#each_cons的替代解决方案:

def array_to_ranges(arr)
  return [] if arr.empty?  
  seq, i = [[arr[0]]], 0  
  arr.each_cons(2) { |x,y| y-x == 1 ? seq[i] << y : seq[i+=1] = [y] }    
  seq.map { |range| range[0]..range[-1] }    
end

请注意,如果seq中存在任何单例,它也会将这些单例转换为范围。例如,如果arr = [1, 2, 3, 5, 7, 8] array_to_ranges(arr)返回[1..3, 5..5, 7..8]。如果输入是空数组,则返回空数组。