slice_beforeで

id:seuzoさんの『連続した日付を範囲形式でまとめる』を読んで自分も作ってみた。

いつまでも使い方が覚えられないEnumerable#slice_beforeを使ってみる。

Ruby1.9.2p136です。

# coding: UTF-8
require 'date'

lists   = " 2010-12-31 , 2011-01-01  ,  2011-01-02 ,2011-01-10, 2011-03-02,2011-03-01,2011-02-28,2011-03-03,2011-03-05,2012-0-00 "
sorted  = [*lists].flatten.flat_map{|list| list.chomp.split(/,/)}
                          .map{|e| Date.parse(e.strip) rescue nil}.compact.sort

prev = sorted.first
result  = sorted.slice_before{|cur| prev, prevprev = cur, prev; prevprev.succ != prev}
                .map{|a| a.minmax.uniq.map{|e| e.strftime("%m/%d")}.join("".encode(Encoding.find("locale")))}
                .join("".encode(Encoding.find("locale")))
puts result
# =>12/31〜01/02、01/10、02/28〜03/03、03/05

数値もやってみる。

# coding: UTF-8

def list2array(*lists, list_delimiter)
  [*lists].flatten.flat_map{|list| list.chomp.split(list_delimiter).map(&:strip)}
end

def array_of(array, converter)
  array.map(&converter).compact.sort
end

def to_rangef(array, formatter)
  prev = array.first
  groups = array.slice_before do |cur|
    prev, prevprev = cur, prev
    prevprev.succ != prev
  end
  groups.map(&formatter)
end

if __FILE__ == $0
  
  lists = DATA.lines.to_a
  
  list_delimiter = /,/
  array = list2array(lists, "11.1,12.3,13.5,14.7,15.9", list_delimiter)
  
  converters = {
    Date:   lambda{|e| require 'date'; Date.parse(e) rescue nil},
    Fixnum: lambda{|e| e.to_i.zero? ? (Integer(e) rescue nil) : e.to_i},
    String: lambda{|e| e.to_s rescue nil},
  }
  array = array_of(array, converters[:Fixnum])
  
  formatters = {
    Date:   lambda{|a| require 'date'; a.minmax.uniq.map{|e| e.strftime("%m/%d")}.join("..")},
    Fixnum: lambda{|a| a.minmax.uniq.map(&:to_s).join("..")},
    String: lambda{|a| a.minmax.uniq.map{|e| %!"%s"!%e}.join("..")},
  }
  groups = to_rangef(array, formatters[:Fixnum])
  
  delimiter = "".encode(Encoding.find("locale"))
  puts groups.join(delimiter)
# =>0..2、4..7、9..15  
end

__END__
1,2,4
6
5
7.0,9.0,10
0
a, ?a

眠くてGiveUp(-_-)zzz