作者: KAWAJI Shinya
日時: 2002/11/15(16:04)
かわじ、です。

初めまして、かな?


> 1,2,3,5,7,8,10,12,14,15,16,17
> 
> の様なソート済番号のリストを,
> 
> 1-3,5,7-8,10,12,14-17
> 
> の様な表現に直すスクリプトを Perl でつくりました.

> そんなわけで「お題」として如何でしょう.


Ruby ですが、このメールの末尾に付けておきます。
使い方は、以下の通り。

-> [1,2,3,5,7,8,10,12,14,15,16,17].to_r.join(',')
"1-3,5,7-8,10,12,14-17"

to_r というメソッドの命名のいい加減さと、Range#to_s の挙動を変更して
しまっていますが、そこは本当に使いたい人が修正して下さるとして、
メリットは、Arrayの構成要素は数字に限らず、Rangeの要素になりうる
オブジェクト(つまり、<=> と succ のメソッドの両方あるオブジェクト)で
あれば、どのようなオブジェクトも要素に出来るというところです(ただし、
もちろん、Array内は全て同じクラスのオブジェクトであることが前提です)。

例えばで、Date オブジェクトですと
( - で連結しているので分かりにくいですが)、

-> [
->   Date.new(2002,1,1),
->   Date.new(2002,1,2),
->   Date.new(2002,1,5),
->   Date.new(2002,1,7),
->   Date.new(2002,1,8),
->   Date.new(2002,1,9),
-> ].to_r.join(',')
"2002-01-01-2002-01-02,2002-01-05,2002-01-07-2002-01-09"

という感じです。

ざくっと作ったので、もっと速いバージョンをどなたか作ってくださることで
しょう・・・


class Range
  def to_s
    [self.begin,self.end].join('-')
  end
end

class Array
  def to_r
    result = []
    from   = nil
    to     = nil
    self.compact.uniq.sort.each{|a|
      if to and to.succ == a
        from ||= to
      elsif to
        result << (from ? Range.new(from,to) : to)
        from = nil
      end
      to = a
    }
    result << (from ? Range.new(from,to) : to)
  end
end