koyoweblog log

2010/10/11 までの http://d.hatena.ne.jp/sasashin の記事をインポートしただけです。

連続した数列を範囲形式にまとめたい

仕様

* 数値は、半角スペースで区切られた文字列で渡されます。
* 続いている部分は、最初の数値と最後の数値を-(ハイフン)で繋いだ表記にします。
* 連続が1回の場合(前の数も後ろの数も連続でない)は、-(ハイフン)では繋ぎません。
* 出力は、「,」(カンマ)と半角スペースで区切られた文字列でなければなりません。


* "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."

回答例は判定用の変数と break 使っているのが気持ち悪いよねー。というわけで、自分でもやってみた。ついでに「ソート済みの数列じゃなくても大丈夫」「同じ数値が含まれていたらまとめる」機能も追加。実行は codepad で。

str = "2 8 5 4 6 2 5" # => "2, 4-6, 8."

p str.split(/\s+/).map{|e| e.to_i}.sort.inject([[]]){|b, e|
  if [nil, e, e-1].index(b.last.last) then b.last << e else b << [e] end
  b
}.map{|e|
  "#{e.first}" + if e.first == e.last then "" else "-#{e.last}" end
}.join(", ") + "."

やっていることは、

  • 文字列を半角スペースで splitする。
  • 文字列の Array を数値の Array にする。
  • 連続した数値の Array を要素にもつ Array を作る。
  • 連続した数値の Array の先頭と末尾の要素が同値ならハイフンなしの文字列、でなければハイフン付きの文字列にして Array に詰める。
  • カンマを区切り文字にして join する。

という感じ。途中でステータスを管理する必要がないところと、メソッドチェーンで一気に書けているところがちょっと気に入ってます。

4/21 追記

再帰なー。inject で書けるやつは、多分再帰で書き直せるんだろうなあ。関数定義するのめんどいからやらないけど。