かなり古い記事だけど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さんの案を参考に一部を訂正、追記しました。

(comment) >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

同じような例題がEnumerable#slice_beforeのヘルプの例になってますねw
http://rurema.clear-code.com/1.9.3/method/Enumerable/i/slice_before.html »smilerubyさん
コメントどうもです! slice_beforeなんて技があったのですね. それにしてもe.mimmax.uniq*’-‘はステキすぎます. それとstr.scan(/\d+/)が正しいですね. 訂正しておきます.

追記ありがとうございます。
slice_beforeは前に同じような例題(日付バージョン)をやった時に覚えましたが、このパターンの処理の時にしか使ったことがありませんw
とはいえ、1.9で導入されたchunkやslice_before等のメソッドを使わずに実装するのは面倒なので、この手のメソッドが用意されているRubyは便利ですよね!



blog comments powered by Disqus
ruby_pack8

100円〜で好評発売中!
M'ELBORNE BOOKS