わさっきhb

大学(教育研究)とか ,親馬鹿とか,和歌山とか,とか,とか.

降順と昇順を組み合わせたソート〜4つのソート方法

降順と昇順を組み合わせたソート - わさっき降順と昇順を組み合わせたソート〜データ生成 - わさっきの続きです.
いよいよソートですが,sortを使うものを一つ,sort_byを使うものを三つ,作りました.

def sort1(array)
  array.sort {|a, b|
    a1 = a[0, 8]
    a2 = a[9, 1]
    b1 = b[0, 8]
    b2 = b[9, 1]
    if a1 != b1
      b1 <=> a1
    else
      a2 <=> b2
    end
  }
end

def sort2(array)
  inv = Hash[%w(0 1 2 3 4 5 6 7 8 9).zip(%w(9 8 7 6 5 4 3 2 1 0))]
  array.sort_by {|a| a.sub(/([0-9])$/) {inv[$1]}}.reverse
end

def sort3(array)
  array.sort_by {|a| a.to_i * 10 - a[-1, 1].to_i}.reverse
end

def sort4(array)
  array.sort_by {|a| ((99999999 - a.to_i) * 10) + a[-1, 1].to_i}
end

sort1は,仕様を忠実にコードにしたものです.ソートの比較で「a<=>b」を使うと,昇順になりますが,逆にして「b<=>a」にすれば,降順になります*1
sort2は,"20080521-1" という文字列を例にすると,最初の8桁とハイフンはそのままで,最後の文字を反転させ(9から引いて8として),"20080521-8" をソート用のキーとします.これを使って配列を並べ替える*2と,昇順になりますので,逆順にするreverseをつけることで,意図どおりにソートすることになります.
sort3は,整数の比較に持っていきます.もとの8桁を整数にして,10倍します.1の位は,最後の桁です.「a.to_i * 10 + (9 - a[-1, 1].to_i)」と書けば,sort2のキーの整数版となりますが,配列対象のどの要素にも「9を足す」というのは無駄ですので,それを取り除いて「a.to_i * 10 - a[-1, 1].to_i」としているわけです."20080521-1" に対しては,200805209という整数値を作ります.sort2と同じく,reverseが必要です.
sort4は,reverseに時間がかかるのなら,キー生成で工夫できないかと思いながら書いたコードです.sort2もsort3も,前の8桁はそのまま,後ろの1桁を反転(9から引く)していましたが,これを,前の8桁は各桁反転,後ろの1桁はそのままにして,整数値のキーを作ります.具体的には,"20080521-1" だと,799194781になります.
実行時間が気になりますが,手元で計ったところ,sort2,sort3,sort4に差はなく,sort1はやや時間がかかります.具体的な計測方法は,また日を改めて.

*1:sortメソッドの呼び出しで,{|a,b|...} を {|b,a|...} と書き換えれば,<=>演算子を使うところはそのまま,とできますが,今回の問題では使えません.

*2:ソート用のキー「を」並べ替えるのではなく,ソート用のキー「で」もとの配列を並べ替えます.