web-dev-qa-db-ja.com

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] }

しかし、その負の符号は適切ではないようです。任意の意見?

255
Waseem

様々な提案された答えについてベンチマークを行うことは常に啓発的です。これが私が見つけたものです:

#!/ usr/bin/Ruby 
 
は 'benchmark'を必要とする
 
 ary = [] 
 1000×{ [。] ary << {:bar => Rand(1000)} 
} 
 
 n = 500 
 Benchmark.bm(20)do | x | 
 x.report( "sort"){n×{ary.sort {| a、b | x.report( "sort reverse"){n×ary.sort {| a、b | b [:bar] <=> a [:bar]}}} 
 a [:bar] <=> b [:bar]} .reverse}} 
 x.report( "sort_by -a [:bar]"){n×{ary.sort_by {| a |}} x.report( "sort_by a [:bar] *  -  1"){n×{ary.sort_by {| a | -a [:bar]}}} 
 a [:bar] *  -  1}}} 
 x.report( "sort_by.reverse!"){n×x {ary.sort_by {| a |}} a [:bar]} .reverse}} 
 end 
 
ユーザシステムの合計実数
 sort 3.960000 0.010000 3.970000(3.990886)
 reverse reverse 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。逆! 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にあります。より新しい、またはより速いマシンはより低い値を持ちますが、相対的な違いは残ります。


これは、新しいハードウェアと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)
522
the Tin Man

簡単に言うと、それは降順の意図を意味します。

descending = -1
a.sort_by { |h| h[:bar] * descending }

(当面はもっと良い方法を考えます);)


a.sort_by { |h| h[:bar] }.reverse!
84
Pablo Fernandez

あなたがすることができます:

a.sort{|a,b| b[:bar] <=> a[:bar]}
51
JRL

どうですか?

 a.sort {|x,y| y[:bar]<=>x[:bar]}

できます!!

irb
>> a = [
?>   { :foo => 'foo', :bar => 2 },
?>   { :foo => 'foo', :bar => 3 },
?>   { :foo => 'foo', :bar => 5 },
?> ]
=> [{:bar=>2, :foo=>"foo"}, {:bar=>3, :foo=>"foo"}, {:bar=>5, :foo=>"foo"}]

>>  a.sort {|x,y| y[:bar]<=>x[:bar]}
=> [{:bar=>5, :foo=>"foo"}, {:bar=>3, :foo=>"foo"}, {:bar=>2, :foo=>"foo"}]
6
OscarRyz

基本的に2つの選択肢があります。

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

そして

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

ソートキーが一意の場合はどちらの方法でも同じ結果になりますが、reverseの方法では等しいキーの順序が逆になるになります。

例:

a = [{foo: 1, bar: 1},{foo: 2,bar: 1}]
a.sort_by {|h| -h[:bar]}
 => [{:foo=>1, :bar=>1}, {:foo=>2, :bar=>1}]
a.sort_by {|h| h[:bar]}.reverse
 => [{:foo=>2, :bar=>1}, {:foo=>1, :bar=>1}]

あなたはしばしばこれを気にする必要はありませんが、時々あなたは気をつけます。このような振る舞いを避けるために、2番目のソートキーを導入することができます(確かに少なくとも同じソートキーを持つすべてのアイテムに対して一意である必要があります)。

a.sort_by {|h| [-h[:bar],-h[:foo]]}
 => [{:foo=>2, :bar=>1}, {:foo=>1, :bar=>1}]
a.sort_by {|h| [h[:bar],h[:foo]]}.reverse
 => [{:foo=>2, :bar=>1}, {:foo=>1, :bar=>1}]
5
Niklas Hoesl

上記のベンチマークスイートに関して...これらの結果はソートされた配列にも当てはまります。 sort_by/reverse :)

例えば:

# foo.rb
require 'benchmark'

NUM_RUNS = 1000

# arr = []
arr1 = 3000.times.map { { num: Rand(1000) } }
arr2 = 3000.times.map { |n| { num: n } }.reverse

Benchmark.bm(20) do |x|
  { 'randomized'     => arr1,
    'sorted'         => arr2 }.each do |label, arr|
    puts '---------------------------------------------------'
    puts label

    x.report('sort_by / reverse') {
      NUM_RUNS.times { arr.sort_by { |h| h[:num] }.reverse }
    }
    x.report('sort_by -') {
      NUM_RUNS.times { arr.sort_by { |h| -h[:num] } }
    }
  end
end

そしてその結果:

$: Ruby foo.rb
                           user     system      total        real
---------------------------------------------------
randomized
sort_by / reverse      1.680000   0.010000   1.690000 (  1.682051)
sort_by -              1.830000   0.000000   1.830000 (  1.830359)
---------------------------------------------------
sorted
sort_by / reverse      0.400000   0.000000   0.400000 (  0.402990)
sort_by -              0.500000   0.000000   0.500000 (  0.499350)

昇順から降順、およびその逆の簡単な解決策は次のとおりです。

STRINGS

str = ['ravi', 'aravind', 'joker', 'poker']
asc_string = str.sort # => ["aravind", "joker", "poker", "ravi"]
asc_string.reverse # => ["ravi", "poker", "joker", "aravind"]

数字

digit = [234,45,1,5,78,45,34,9]
asc_digit = digit.sort # => [1, 5, 9, 34, 45, 45, 78, 234]
asc_digit.reverse # => [234, 78, 45, 45, 34, 9, 5, 1]
0
Ravistm

IPS;で速度を測定したい人のために

require 'benchmark/ips'

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

Benchmark.ips do |x|
  x.report("sort")               { ary.sort{ |a,b| b[:bar] <=> a[:bar] } }
  x.report("sort reverse")       { ary.sort{ |a,b| a[:bar] <=> b[:bar] }.reverse }
  x.report("sort_by -a[:bar]")   { ary.sort_by{ |a| -a[:bar] } }
  x.report("sort_by a[:bar]*-1") { ary.sort_by{ |a| a[:bar]*-1 } }
  x.report("sort_by.reverse!")   { ary.sort_by{ |a| a[:bar] }.reverse }
  x.compare!
end

そして結果:

Warming up --------------------------------------
                sort    93.000  i/100ms
        sort reverse    91.000  i/100ms
    sort_by -a[:bar]   382.000  i/100ms
  sort_by a[:bar]*-1   398.000  i/100ms
    sort_by.reverse!   397.000  i/100ms
Calculating -------------------------------------
                sort    938.530  (± 1.8%) i/s -      4.743k in   5.055290s
        sort reverse    901.157  (± 6.1%) i/s -      4.550k in   5.075351s
    sort_by -a[:bar]      3.814k (± 4.4%) i/s -     19.100k in   5.019260s
  sort_by a[:bar]*-1      3.732k (± 4.3%) i/s -     18.706k in   5.021720s
    sort_by.reverse!      3.928k (± 3.6%) i/s -     19.850k in   5.060202s

Comparison:
    sort_by.reverse!:     3927.8 i/s
    sort_by -a[:bar]:     3813.9 i/s - same-ish: difference falls within error
  sort_by a[:bar]*-1:     3732.3 i/s - same-ish: difference falls within error
                sort:      938.5 i/s - 4.19x  slower
        sort reverse:      901.2 i/s - 4.36x  slower
0
mpospelov