sort_by使おうよ

僅か30分で3つのバグ - Rubyの落し穴 - — ありえるえりあ

arr = []
arr.push({:x=>1,:y=>1}).push({:x=>2,:y=>2}).push({:x=>1,:y=>9}).push({:x=>5,:y=>0}).push({:x=>1,:y=>3})

x要素がプライマリなソートキーで、y要素がセカンダリなソートキーだとして、次のようにソ ート処理を書きました。

sort_byならもっと簡単に書ける。

def sort1(arr)
  arr.sort do |a,b|
    next 1 if !a[:x]
    next -1 if !b[:x]
    cmp = a[:x]<=>b[:x]
    if cmp != 0
      cmp
    else
      next 1 if !a[:y]
      next -1 if !b[:y]
      a[:y]<=>b[:y]
    end
  end
end

arr = [ {:x=>1,:y=>1}, {:x=>2,:y=>2}, {:x=>1,:y=>9}, {:x=>5,:y=>0}, {:x=>1,:y=>3} ]
arr2 = [ {:x=>nil,:y=>0}, {:x=>1,:y=>nil} ]
sort1(arr)  # => [{:x=>1, :y=>1}, {:x=>1, :y=>3}, {:x=>1, :y=>9}, {:x=>2, :y=>2}, {:x=>5, :y=>0}]
sort1(arr2) # => [{:x=>1, :y=>nil}, {:x=>nil, :y=>0}]

# nil=0とみなせばこう。
def sort2(arr)
  arr.sort_by do |h|
    [h[:x].to_i, h[:y].to_i]
  end
end

sort2(arr)  # => [{:x=>1, :y=>1}, {:x=>1, :y=>3}, {:x=>1, :y=>9}, {:x=>2, :y=>2}, {:x=>5, :y=>0}]
sort2(arr2) # => [{:x=>nil, :y=>0}, {:x=>1, :y=>nil}]

# nilを最後にするならばかでかい値にする。
def sort3(arr)
  nil_value = 100000000000
  arr.sort_by do |h|
    [h[:x]||nil_value, h[:y]||nil_value]
  end
end

sort3(arr)  # => [{:x=>1, :y=>1}, {:x=>1, :y=>3}, {:x=>1, :y=>9}, {:x=>2, :y=>2}, {:x=>5, :y=>0}]
sort3(arr2) # => [{:x=>1, :y=>nil}, {:x=>nil, :y=>0}]

# 重み関数を計算させると爆速。
def sort4(arr)
  big = 100000000000
  arr.sort_by do |h|
    (h[:x]||big)*big + (h[:x]||big)
  end
end

sort4(arr)  # => [{:x=>1, :y=>1}, {:x=>1, :y=>3}, {:x=>1, :y=>9}, {:x=>2, :y=>2}, {:x=>5, :y=>0}]
sort4(arr2) # => [{:x=>1, :y=>nil}, {:x=>nil, :y=>0}]


ary4 = Array.new(3000){ {:x => rand(10), :y => rand(10000)} }
require 'benchmark'
Benchmark.bmbm do |b|
  b.report("sort1") { sort1(ary4) }
  b.report("sort2") { sort2(ary4) }
  b.report("sort3") { sort3(ary4) }
  b.report("sort4") { sort4(ary4) }
end
# >> Rehearsal -----------------------------------------
# >> sort1   0.080000   0.000000   0.080000 (  0.073833)
# >> sort2   0.060000   0.000000   0.060000 (  0.063894)
# >> sort3   0.070000   0.000000   0.070000 (  0.069434)
# >> sort4   0.010000   0.000000   0.010000 (  0.015070)
# >> -------------------------------- total: 0.220000sec
# >> 
# >>             user     system      total        real
# >> sort1   0.080000   0.000000   0.080000 (  0.073774)
# >> sort2   0.070000   0.000000   0.070000 (  0.065415)
# >> sort3   0.060000   0.000000   0.060000 (  0.063259)
# >> sort4   0.010000   0.000000   0.010000 (  0.015426)