如何在Ruby中按降序排列数组

如何在Ruby中按降序排列数组,ruby,sorting,Ruby,Sorting,我有一个哈希数组: [ { :foo => 'foo', :bar => 2 }, { :foo => 'foo', :bar => 3 }, { :foo => 'foo', :bar => 5 }, ] 我试图根据每个散列中:bar的值按降序对该数组进行排序 我正在使用sort\u by对上述数组进行排序: a.sort_by { |h| h[:bar] } 但是,这会按升序对数组进行排序。如何按降序排序 一个解决方案是执行以下操作: a.

我有一个哈希数组:

[
  { :foo => 'foo', :bar => 2 },
  { :foo => 'foo', :bar => 3 },
  { :foo => 'foo', :bar => 5 },
]
我试图根据每个散列中
:bar
的值按降序对该数组进行排序

我正在使用
sort\u by
对上述数组进行排序:

a.sort_by { |h| h[:bar] }
但是,这会按升序对数组进行排序。如何按降序排序

一个解决方案是执行以下操作:

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

但是这个负号似乎不合适。

只是一个简单的事情,它表示降序的意图

descending = -1
a.sort_by { |h| h[:bar] * descending }
(同时会想出更好的办法);)


那么:

 a.sort {|x,y| y[:bar]<=>x[: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"}]
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}]
>>排序{x,y{y[:bar]x[:bar]}
=>[{:bar=>5,:foo=>foo},{:bar=>3,:foo=>foo},{:bar=>2,:foo=>foo}]
您可以执行以下操作:

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

对各种建议答案进行基准测试总是很有启发性的。以下是我发现的:

#!/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) 这些都在旧的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)

在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方法实际上并不返回一个反向数组-它返回一个枚举数,该枚举数只从末尾开始并向后运行

资料来源为:


do*p2--=*p1++;而(-len>0)将指针以相反的顺序复制到元素,因此数组被反转。

关于提到的基准测试套件,这些结果也适用于排序数组

sort\u 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 = [{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}]
虽然你通常不需要关心这件事,但有时你确实需要。为了避免这种行为,您可以引入第二个排序键(至少对于具有相同排序键的所有项目,该排序键必须是唯一的):


对于那些喜欢用IPS测量速度的人;)


从升序到降序以及从降序到降序的简单解决方案是:

字符串

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]
数字

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]


是的,它实际上是有效的,但我认为PO希望显示代码的意图(他已经有了一个有效的解决方案)。虽然
sort
会起作用,但排序立即值时只会更快。如果你必须挖掘它们,
sort\u by
会更快。看看基准测试。考虑到其他选项,我仍然认为-h[:bar]是最优雅的。你不喜欢它的什么地方?我对传达代码的意图更感兴趣。@Waseem我可以麻烦你更新接受的答案吗?@Waseem当前的答案没有问题。正好有一个更好的答案。铁皮人的答案更为详尽,并表明
sort\u by.reverse
比目前公认的答案效率更高。我相信它也更好地解决了您上面提到的“传达代码意图”的问题。除此之外,铁皮人还更新了他对ruby当前版本的回答。这个问题已经被浏览了超过1.5万次。如果你能为每位观众节省1秒钟的时间,我认为这是值得的。@collindo谢谢,我做到了。:)巴勃罗,找到更好的方法干得不错!请看我做的基准测试。第一种方法更快(尽管可能更难看),因为它只循环一次。至于第二个,你不需要!,这是用于就地操作。如果在反转后不使用bang,则不会反转数组,而是创建另一个反转的数组。但是使用
sort\u by
的全部意义在于避免了多次运行比较函数-1<代码>排序依据
更高效、更可读。否定值或在末尾进行反转将更快、更易读。我喜欢这个答案,因为
*-1
不适用于所有值(例如时间),而
反转
将对排序为同等有用的值重新排序。谢谢你的额外努力。我喜欢人们提供这样的基准证明!!令人惊叹的!“我喜欢人们提供这样的基准证明!!”我也喜欢,因为这样我就不必了。@theTinMan你能提供一个TL;谢谢你的回答。所有这些基准信息都非常有用。但是一个TL;答案上方的DR将对那些只想要答案的人有用。我知道他们应该阅读整个解释,我想他们会的。仍然是TL;DR将对IMHO非常有用。谢谢你的努力。我同意@Waseem。尽管这个答案经过了充分的研究,OP并没有问“在Ruby中进行降序排序的最快方法是什么”。一个TL;顶部的DR显示了简单的用法,然后是基准测试,这将改进IMO的答案。您应该能够按{}进行排序。反向!(reverse with the bang创建了一个新的数组,当然我希望这个数组会慢一些)+1指出
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}]
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
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]