将数组转换为连续数字范围

6

我正在尝试编写一个Ruby函数,将唯一数字的数组转换为连续数字范围。

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

这似乎不太难,但我想知道是否有更好的方法。


6
比什么更好?你还没有发布你的解决方案。 - Sergio Tulentsev
2
当您在SO上请求代码时,我们期望您提供已经尝试过的搜索答案的内容。我们需要知道您正在付出一些努力,而不仅仅是在寻找代码。 - the Tin Man
3个回答

4

这是如何使用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]

我非常喜欢这个解决方案。 - Sam Kong
1
它似乎已经出现在文档中了:https://apidock.com/ruby/Enumerable/slice_before - JLB

3

这是我在处理IP地址范围时写的一些内容:

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

true传递给to_ranges,它将不会将单个元素转换为单元素范围。


好方法!虽然我很好奇,什么情况下您不希望参数为true。 - Mark Thomas
我曾经有过需要将数组缩小为范围和值的时候。这不仅适用于整数;就像我在代码中所说的那样,我用它将IP地址转换为网络范围和单个IP地址。其他时候,我只想要范围。 - the Tin Man
运行得很好。我爱你,谢谢你为我节省了几分钟的算法时间。 - mld.oscar

2
这里有一个与你提出的问题相同的解决方案。链接中的代码比你需要的工作要多一些(数字不需要排序或连续),但它可以解决问题。或者,你可以使用@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

嗯...实际上,我认为NewAlexandria只是将代码从你的链接中复制到问题中...所以,从技术上讲,这是建议的代码... - JDB

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接