我希望将带有间隙的数字数组转换为Ruby中多个范围的数组。每个范围应确定序列中的间隙。假设你有阵列:
[1,2,3,5,6,8,9,10,11,12]
预期结果为:[1-3,5-6,8-12]
我无法提出解决问题的好主意。我怎样才能解决这个问题?
答案 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]
。如果输入是空数组,则返回空数组。