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

三浦義太郎(KBMJ)
2008-04-17 08:00:00
  • このエントリーをはてなブックマークに追加
最新特集【一覧】

問題

 並んだ数字を確認用にわかりやすくする為に、ソートされた数字の列をハイフンで繋ぐにはどうすればよいでしょうか?

 数列は見づらいものです。ただ数字を並べているだけでは、ソートしてもどの数字が抜けているかを判断するために、順に目で追ってチェックする必要があります。

 rubyではそういった配列をわかりやすく表現する為に「1...3」と表現する方法が用意されているのですが、利用者は、「1 2 3 5」というような数字の羅列を入れてくるかもしれません。さて、rubyでこれらをハイフンで繋ぐためにはどのように処理する必要があるでしょうか。

仕様

  • 数値は、半角スペースで区切られた文字列で渡されます。
  • 続いている部分は、最初の数値と最後の数値を-(ハイフン)で繋いだ表記にします。
  • 連続が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."

回答例

a = $*[0].split(' ').map{|i|i.to_i}+[nil]
i = a[0]
p a.inject([a[0].to_s]){|r, v|
  if i != v
   r << r.pop + "-#{i-1}" if 2 <= i - r.last.to_i
   break r unless v
   i = v
   r << i.to_s
  end
 i +=1
 r
}.join(', ')+'.'

判定用の変数を配列の値と違うものになるまで増分していき、連続を判定しています。連続でなくなった段階で配列の値を出力用の配列と判定用の変数に入れて同様の処理を繰り返していきます。判定用の変数の値と出力用の配列の最後の値(連続開始の値)の差が2以上の場合に「-[数字]」の文字列を追加します。

  • $*にはコマンドライン引数の値の配列が入ります。
  • injectは以前のブロックの実行結果を引数と受け取って順次処理していくことができる関数です。あまり使われていないようですが、結構便利な関数です。

    p [1,2,3,4,5].inject(0) {|result, item| result + item } => 15

 ほかによい方法があればコメントしてみてください。

  • 新着記事
  • 特集
  • ブログ