sort_by

sort_by

ri

 ----------------------------------------------------- Enumerable#sort_by
      enum.sort_by {| obj | block }    => array
 ------------------------------------------------------------------------
      Sorts _enum_ using a set of keys generated by mapping the values in
      _enum_ through the given block.
 
         %w{ apple pear fig }.sort_by {|word| word.length}
                      #=> ["fig", "pear", "apple"]
 
      The current implementation of  sort_by  generates an array of
      tuples containing the original collection element and the mapped
      value. This makes  sort_by  fairly expensive when the keysets are
      simple
 
         require 'benchmark'
         include Benchmark
      
         a = (1..100000).map {rand(100000)}
      
         bm(10) do |b|
           b.report("Sort")    { a.sort }
           b.report("Sort by") { a.sort_by {|a| a} }
         end
 
      _produces:_
 
         user     system      total        real
         Sort        0.180000   0.000000   0.180000 (  0.175469)
         Sort by     1.980000   0.040000   2.020000 (  2.013586)
 
      However, consider the case where comparing the keys is a
      non-trivial operation. The following code sorts some files on
      modification time using the basic  sort  method.
 
         files = Dir["*"]
         sorted = files.sort {|a,b| File.new(a).mtime <=> File.new(b).mtime}
         sorted   #=> ["mon", "tues", "wed", "thurs"]
 
      This sort is inefficient: it generates two new  File  objects
      during every comparison. A slightly better technique is to use the
       Kernel#test  method to generate the modification times directly.
 
         files = Dir["*"]
         sorted = files.sort { |a,b|
           test(?M, a) <=> test(?M, b)
         }
         sorted   #=> ["mon", "tues", "wed", "thurs"]
 
      This still generates many unnecessary  Time  objects. A more
      efficient technique is to cache the sort keys (modification times
      in this case) before the sort. Perl users often call this approach
      a Schwartzian Transform, after Randal Schwartz. We construct a
      temporary array, where each element is an array containing our sort
      key along with the filename. We sort this array, and then extract
      the filename from the result.
 
         sorted = Dir["*"].collect { |f|
            [test(?M, f), f]
         }.sort.collect { |f| f[1] }
         sorted   #=> ["mon", "tues", "wed", "thurs"]
 
      This is exactly what  sort_by  does internally.
 
         sorted = Dir["*"].sort_by {|f| test(?M, f)}
         sorted   #=> ["mon", "tues", "wed", "thurs"]
 

refe

 Enumerable#sort_by
 --- sort_by {|item| ... }   ruby 1.7 feature
 
     ブロックの評価結果を <=> メソッドで比較することで、self を昇
     順にソートします。ソートされた配列を新たに生成して返します。これは、
     以下とほぼ同じ動作をします。
 
       class Array
         def sort_by
           self.collect {|i| [yield(i), i] }.
              sort {|a,b| a[0] <=> b[0] }.
              collect! {|i| i[1]}
         end
       end
 
     sort_by を使わない以下の例では比較を行う度に downcase が実
     行されます。従って downcase の実行速度が遅ければ sort の速度が
     致命的に低下します。
 
       p ["BAR", "FOO", "bar", "foo"].sort {|a,b| a.downcase <=> b.downcase }
 
     一方次のように sort_by を使うと downcase の実行回数は要素数と
     同じです。つまり、その部分の実行時間は O(n) のオーダーです。
 
       p ["BAR", "FOO", "bar", "foo"].sort_by {|v| v.downcase }
 
     以下の、実行回数の検証結果を参照してみてください。
 
       class Integer
         def count
           $n  = 1
           self
         end
       end
 
       ary = []
       1.upto(1000) {|v| ary << rand(v) }
 
       $n = 0
       ary.sort {|a,b| a.count <=> b.count }
       p $n          # => 18200
 
       $n = 0
       ary.sort_by {|v| v.count }
       p $n          # => 1000
 
     Enumerable#sort_by は安定ではありません (unstable sort)。
 
     註: 比較結果が同じ要素は元の順序通りに並ぶソートを
     「安定なソート (stable sort)」と言います。
 
     なお、sort_by を以下のように使うと安定なソートを実装できます。
 
       i = 0
       ary.sort_by {|v| [v, i  = 1] }