Сортировка массива в порядке убывания в 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] }
но этот отрицательный знак не кажется соответствующий. Есть мнения?
7 ответов:
это всегда поучительно, чтобы сделать тест на различных предложенных ответов. Вот что я узнал:
#!/usr/bin/ruby require 'benchmark' 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 } } end user system total real sort 3.960000 0.010000 3.970000 ( 3.990886) sort 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.reverse! 0.650000 0.000000 0.650000 ( 0.654447)Я думаю, что это интересно, что @Пабло
sort_by{...}.reverse!самый быстрый. Перед началом теста я думал, что это будет медленнее, чем "-a[:bar]" но отрицание значения, оказывается, занимает больше времени, чем это делает, чтобы обратить весь массив за один проход. Это не большая разница, но каждое небольшое ускорение помогает.
обратите внимание, что эти результаты отличаются в 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)
новые результаты выполнения вышеуказанного кода с использованием Ruby 2.2.1 on более поздний Macbook Pro. Опять же, точные цифры не важны, это их отношения:
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 {|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"}]
относительно упомянутого набора тестов... эти результаты также сохраняются для отсортированных массивов. 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)
Я вижу, что у нас есть (помимо прочего) в основном два варианта:
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}]а вы часто не нужно заботиться об этом, иногда вы делаете. Чтобы избежать такого поведения, вы можете ввести второй ключ сортировки (это точно должен быть уникальным, по крайней мере для всех элементов, которые имеют тот же ключ сортировки):
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}]
для тех людей, которые любят измерять скорость в 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
Comments