Rubyで連続数字をハイフンでつなぐよ

かなり古い記事だけどRubyを使って
連続数字をハイフンでつなぐ方法が出てたよ


Rubyでどう書く?:連続した数列を範囲形式にまとめたい - builder


要するにスペース区切りの数字列を
数字が連続する場合にその箇所をハイフンにする
という話だよ

"1 2 3" => "1-3."
"1 2 3 5 7 8" => "1-3, 5, 7-8."
"1 3 4 5 7" => "1, 3-5, 7."

解答例が出てたけどなんかしっくり来なかったので
自分なりの違う方法を考えてみたよ


まず数字文字列を数字の配列に変換するよ

str = "1 3 4 5 7"
nums = str.scan(/\d+/).map(&:to_i) # => [1, 3, 4, 5, 7]


次にこれを
配列のインデックスに対応付けて配置し直すよ

nums.inject([]) { |arr, n| arr[n-1] = n; arr } # => [1, nil, 3, 4, 5, nil, 7]

これで数字が連続しないところにはnilが入るよ


次にnilのところで配列を分けるよ

nums = nums.chunk { |n| !n.nil? || nil }.to_a # => [[true, [1]], [true, [3, 4, 5]], [true, [7]]]

Enumerable#chunkを使うよ
そのブロックでは要素がnilの場合はnilを返すようにして
その要素を捨てるよ


次に各要素の長さに応じた加工を施して文字列にするよ

nums = nums.map { |_, gr| gr.size>1 ? "#{gr.first}-#{gr.last}" : "#{gr.first}" } # => ["1", "3-5", "7"]


最後にこれをつないで完成だよ

nums.join(', ') + '.' # => "1, 3-5, 7."


まとめると次のようになるよ



やっぱりRubyは便利だね!


id:smilerubyさんから
Enumerable#slice_beforeを使った例を頂いたので
併せて載せておくよ

def hyphenize(str)
 nums = str.scan(/\d+/).map(&:to_i).sort
 nums.slice_before(num: nums.first) { |e, prev|
   prev[:num], prevprev = e, prev[:num]
   prevprev.succ != prev[:num]
 }.map{ |e| e.minmax.uniq * '-' } * ', ' + '.'
end

hyphenize("1 3 4 5 7") # => "1, 3-5, 7."

e.minmax.uniq*'-'がステキだよね!


(追記:2012-1-7) id:smilerubyさんの案を参考に一部を訂正、追記しました。