sort_byとsort

["2-5", "1-10", "2-1", "3-3", "1-3"].sort

=> ["1-10", "1-3", "2-1", "2-5", "3-3"]

そうじゃなくて、ハイフンの前の数字の昇順→ハイフンの後ろの数字の昇順で並んで欲しい。そんなときはブロック引数を使う。

["2-5", "1-10", "2-1", "3-3", "1-3"].sort { |a, b|
    as = a.split("-")
    bs = b.split("-")
    r = as.first.to_i - bs.first.to_i
    r != 0 ? r : as.last.to_i - bs.last.to_i }

=> ["1-3", "1-10", "2-1", "2-5", "3-3"]

また、このソートは

["2-5", "1-10", "2-1", "3-3", "1-3"].sort_by{ |i| i.split("-").map{ |e| e.to_i } }

と、sort_by を使って書ける。sort_by は配列の各要素からソート用のオブジェクトを作り、そのソート用オブジェクト同士を<=>で比較した結果で元の配列をソートするもの。

ところでRubyのリファレンスマニュアルには、sort のブロックは比較の度に呼ばれるのでブロックの処理が重いと sort が致命的に重くなるが、sort_by のブロックは要素の数しか呼ばれないので実行時間は O(n) のオーダーで済むと書かれている。

しかし、この程度のブロックの処理ならば、どちらで書いても差はない(余計な処理がないためか、むしろsortの方がわずかに速い)ので、気にせずに書きやすい方で書くのがいいと思う。この場合、コードが簡潔なsort_byの方が良い感じだが、何をやってるのか一見して分かりにくいのが欠点か。