一意の数値の配列を連続した数値の範囲に変換する Ruby 関数を作成しようとしています。
[1, 2, 3, 5, 6, 8, 9] => [(1..3), (5..6), (8..9)]
難しそうに見えませんが、もっと良い方法があれば知りたいです。
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]
これは、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
、個々の要素が 1 つの要素の範囲に変換されません。
これは、あなたが尋ねているのと同じ質問に対する解決策です。リンクされたコードは、必要以上に多くの作業を行います (数値を並べ替えたり、連続させたりする必要はありません) が、うまく機能します。または、@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