Rubyで配列を降順でソートする方法 質問する

Rubyで配列を降順でソートする方法 質問する

ハッシュの配列があります:

[
  { :foo => 'foo', :bar => 2 },
  { :foo => 'foo', :bar => 3 },
  { :foo => 'foo', :bar => 5 },
]

:barこの配列を各ハッシュの値に従って降順でソートしようとしています。

上記の配列をソートするために使用していますsort_by:

a.sort_by { |h| h[:bar] }

しかし、これは配列を昇順で並べ替えます。降順で並べ替えるにはどうすればよいでしょうか?

1 つの解決策は、次の操作を実行することです。

a.sort_by { |h| -h[:bar] }

しかし、そのマイナス記号は適切ではないようです。

ベストアンサー1

提案されたさまざまな回答をベンチマークしてみると、いつも勉強になります。私が発見したことは次のとおりです。

ruby は、

「ベンチマーク」が必要

ary = []
1000倍{
  arry << {:bar => rand(1000)}
}

500人
ベンチマーク.bm(20) を実行する |x|
  x.report("sort") { n.times { ary.sort{ |a,b| b[:bar] <=> a[:bar] } } }
  x.report("逆順に並べ替え") { n.times { ary.sort{ |a,b| a[:bar] <=> b[:bar] }.reverse } }
  x.report("sort_by -a[:bar]") { n.times { ary.sort_by{ |a| -a[:bar] } } }
  x.report("sort_by a[:bar]*-1") { n.times { ary.sort_by{ |a| a[:bar]*-1 } } }
  x.report("sort_by.reverse!") { n.times { ary.sort_by{ |a| a[:bar] }.reverse } }
終わり

                          ユーザーシステム合計実数
ソート 3.960000 0.010000 3.970000 ( 3.990886)
逆順に並べ替え 4.040000 0.000000 4.040000 ( 4.038849)
ソート順 -a[:bar] 0.690000 0.000000 0.690000 ( 0.692080)
ソート_by a[:bar]*-1 0.700000 0.000000 0.700000 ( 0.699735)
逆順に並べ替え! 0.650000 0.000000 0.650000 ( 0.654447)

@Pablo のが一番速いというのは興味深いと思いますsort_by{...}.reverse!。テストを実行する前は、" -a[:bar]" よりも遅いと思っていましたが、値を反転すると、配列全体を 1 回のパスで反転するよりも時間がかかることがわかりました。大した違いはありませんが、少しでもスピードが上がると役立ちます。


これらの結果はRuby 1.9では異なることに注意してください。

Ruby 1.9.3p194 (2012-04-20 リビジョン 35410) [x86_64-darwin10.8.0] の結果は次のとおりです。

                           user     system      total        real
sort                   1.340000   0.010000   1.350000 (  1.346331)
sort reverse           1.300000   0.000000   1.300000 (  1.310446)
sort_by -a[:bar]       0.430000   0.000000   0.430000 (  0.429606)
sort_by a[:bar]*-1     0.420000   0.000000   0.420000 (  0.414383)
sort_by.reverse!       0.400000   0.000000   0.400000 (  0.401275)

これらは古い MacBook Pro のものです。新しい、またはより高速なマシンでは値は低くなりますが、相対的な差は残ります。


以下は、新しいハードウェアと Ruby のバージョン 2.1.1 で少し更新されたバージョンです。

#!/usr/bin/ruby

require 'benchmark'

puts "Running Ruby #{RUBY_VERSION}"

ary = []
1000.times {
  ary << {:bar => rand(1000)}
}

n = 500

puts "n=#{n}"
Benchmark.bm(20) do |x|
  x.report("sort")               { n.times { ary.dup.sort{ |a,b| b[:bar] <=> a[:bar] } } }
  x.report("sort reverse")       { n.times { ary.dup.sort{ |a,b| a[:bar] <=> b[:bar] }.reverse } }
  x.report("sort_by -a[:bar]")   { n.times { ary.dup.sort_by{ |a| -a[:bar] } } }
  x.report("sort_by a[:bar]*-1") { n.times { ary.dup.sort_by{ |a| a[:bar]*-1 } } }
  x.report("sort_by.reverse")    { n.times { ary.dup.sort_by{ |a| a[:bar] }.reverse } }
  x.report("sort_by.reverse!")   { n.times { ary.dup.sort_by{ |a| a[:bar] }.reverse! } }
end

# >> Running Ruby 2.1.1
# >> n=500
# >>                            user     system      total        real
# >> sort                   0.670000   0.000000   0.670000 (  0.667754)
# >> sort reverse           0.650000   0.000000   0.650000 (  0.655582)
# >> sort_by -a[:bar]       0.260000   0.010000   0.270000 (  0.255919)
# >> sort_by a[:bar]*-1     0.250000   0.000000   0.250000 (  0.258924)
# >> sort_by.reverse        0.250000   0.000000   0.250000 (  0.245179)
# >> sort_by.reverse!       0.240000   0.000000   0.240000 (  0.242340)

最新の Macbook Pro で Ruby 2.2.1 を使用して上記のコードを実行した新しい結果。繰り返しますが、正確な数値は重要ではなく、それらの関係が重要です。

Running Ruby 2.2.1
n=500
                           user     system      total        real
sort                   0.650000   0.000000   0.650000 (  0.653191)
sort reverse           0.650000   0.000000   0.650000 (  0.648761)
sort_by -a[:bar]       0.240000   0.010000   0.250000 (  0.245193)
sort_by a[:bar]*-1     0.240000   0.000000   0.240000 (  0.240541)
sort_by.reverse        0.230000   0.000000   0.230000 (  0.228571)
sort_by.reverse!       0.230000   0.000000   0.230000 (  0.230040)

Mid-2015 MacBook Pro の Ruby 2.7.1 用に更新されました:

Running Ruby 2.7.1
n=500     
                           user     system      total        real
sort                   0.494707   0.003662   0.498369 (  0.501064)
sort reverse           0.480181   0.005186   0.485367 (  0.487972)
sort_by -a[:bar]       0.121521   0.003781   0.125302 (  0.126557)
sort_by a[:bar]*-1     0.115097   0.003931   0.119028 (  0.122991)
sort_by.reverse        0.110459   0.003414   0.113873 (  0.114443)
sort_by.reverse!       0.108997   0.001631   0.110628 (  0.111532)

...reverse メソッドは、実際には反転された配列を返すのではなく、末尾から開始して逆方向に動作する列挙子を返します。

情報源Array#reverseは:

               static VALUE
rb_ary_reverse_m(VALUE ary)
{
    long len = RARRAY_LEN(ary);
    VALUE dup = rb_ary_new2(len);

    if (len > 0) {
        const VALUE *p1 = RARRAY_CONST_PTR_TRANSIENT(ary);
        VALUE *p2 = (VALUE *)RARRAY_CONST_PTR_TRANSIENT(dup) + len - 1;
        do *p2-- = *p1++; while (--len > 0);
    }
    ARY_SET_LEN(dup, RARRAY_LEN(ary));
    return dup;
}

do *p2-- = *p1++; while (--len > 0);私の C の記憶が正しければ、要素へのポインターを逆の順序でコピーしているので、配列は逆になります。

おすすめ記事