[解決済み] Rubyで配列を降順にソートする方法
質問
ハッシュの配列があります。
[
{ :foo => 'foo', :bar => 2 },
{ :foo => 'foo', :bar => 3 },
{ :foo => 'foo', :bar => 5 },
]
の値に応じて、この配列を降順に並べ替えようとしています。
:bar
を各ハッシュの中に入れてください。
を使っています。
sort_by
を使用して、上記の配列をソートします。
a.sort_by { |h| h[:bar] }
しかし、これでは配列の昇順でソートされてしまいます。どうすれば降順にソートされるのでしょうか?
一つの解決策として、次のようにしました。
a.sort_by { |h| -h[:bar] }
しかし、そのマイナス記号は適切ではないようです。
どのように解決するのですか?
様々な回答案についてベンチマークを行うのは、いつも勉強になります。以下は、私が発見したことです。
#!/usr/bin/ruby require 'ベンチマーク' ary = [ ]です。 1000.times { ary << {:bar => rand(1000)}. n = 500 Benchmark.bm(20) do |x|. x.report("sort") { n.times { ary.sort{ |a,b| b[:bar] <=> a[:bar] } }. } x.report("sort reverse") { 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) sort_by -a[:bar] 0.690000 0.000000 0.690000 ( 0.692080) sort_by a[:bar]*-1 0.700000 0.000000 0.700000 ( 0.699735) sort_by.reverse! 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 revision 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を使用したものです。新しいマシンや高速なマシンでは値が下がりますが、相対的な差は残ります。
新しいハードウェアと2.1.1バージョンのRubyで、少し更新したバージョンを紹介します。
#!/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言語の記憶が正しければ、要素へのポインタを逆順にコピーしているので、配列は逆になっています。
関連
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】StringからIntegerへの暗黙の変換がない(TypeError)?
-
[解決済み】rubyの1e18という数字は何を意味するのか?
-
[解決済み] -bashです。/Users/myname/.bash_profile: パーミッションが拒否されました
-
[解決済み] RubyのHashをキーでアルファベット順にソートする方法
-
[解決済み] Rubyの配列から文字列への変換
-
[解決済み] 配列をアルファベット順に並べるには?
-
[解決済み] Rubyで日付文字列をパースする
-
[解決済み] Rubyで中央値を計算する
-
[解決済み] ルビー、すべての単語の最初の文字を大文字にする
-
[解決済み] .rb(Ruby)ファイルを表示するにはどうすればよいですか?