私は以下のようなハッシュの配列を持っています
[
{ :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] }
しかし、その負の符号は適切ではないようです。任意の意見?
様々な提案された答えについてベンチマークを行うことは常に啓発的です。これが私が見つけたものです:
#!/ 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)
簡単に言うと、それは降順の意図を意味します。
descending = -1
a.sort_by { |h| h[:bar] * descending }
(当面はもっと良い方法を考えます);)
a.sort_by { |h| h[:bar] }.reverse!
あなたがすることができます:
a.sort{|a,b| b[:bar] <=> a[:bar]}
どうですか?
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"}]
基本的に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}]
上記のベンチマークスイートに関して...これらの結果はソートされた配列にも当てはまります。 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]
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