web-dev-qa-db-fra.com

Tri d'un tableau en ordre décroissant dans Ruby

J'ai un tableau de hachages comme suit

[
  { :foo => 'foo', :bar => 2 },
  { :foo => 'foo', :bar => 3 },
  { :foo => 'foo', :bar => 5 },
]

J'essaie de trier le tableau ci-dessus dans l'ordre décroissant en fonction de la valeur de :bar dans chaque hachage.

J'utilise sort_by comme suit pour trier le tableau ci-dessus.

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

Cependant, ci-dessus, le tableau est trié par ordre croissant. Comment puis-je le trier par ordre décroissant?

Une solution consistait à faire ce qui suit:

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

Mais ce signe négatif ne semble pas approprié. Des vues?

259
Waseem

Il est toujours intéressant de faire un point de repère sur les différentes réponses suggérées. Voici ce que j'ai découvert:

 #!/usr/bin/Ruby 
 
 nécessite un "repère" 
 
 ary = [] 
 1000. fois { 
 << <<: bar => Rand (1000)} 
} 
 
 n = 500 
 Indice de référence.bm (20) do | x | 
 x.report ("sort") {n.times {ary.sort {| a, b | b [: barre] <=> a [: barre]}}} 
 x.report ("sorte de tri inverse") {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 
 
 total du système utilisateur réel 
 trié 3.960000 0.010000 3.970000 (3.990886) 
 trié inversé 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. sens inverse! 0,650000 0,000000 0,650000 (0,654447) 

Je trouve intéressant que le sort_by{...}.reverse! de @ Pablo soit le plus rapide. Avant d'exécuter le test, je pensais que ce serait plus lent que "-a[:bar]", mais l'annulation de la valeur prend plus de temps que celle nécessaire pour inverser le tableau en entier en une seule passe. La différence n’est pas grande, mais chaque petite accélération est utile.


Veuillez noter que ces résultats sont différents dans Ruby 1.9

Voici les résultats de Ruby 1.9.3p194 (révision 2012-04-20, révision 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)

Ce sont sur un ancien MacBook Pro. Les machines les plus récentes ou les plus rapides auront des valeurs plus faibles, mais les différences relatives resteront.


Voici une version légèrement mise à jour du nouveau matériel et de la version 2.1.1 de 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)

Nouveaux résultats exécutant le code ci-dessus à l'aide de Ruby 2.2.1 sur un Macbook Pro plus récent. Encore une fois, les chiffres exacts ne sont pas importants, ce sont leurs relations:

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)
530
the Tin Man

Juste une chose rapide, cela dénote l'intention de l'ordre décroissant.

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

(Pensera à un meilleur moyen entre temps);)


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

Vous pourriez faire:

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

Qu'en est-il de:

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

Ça marche!!

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

Je vois que nous avons (à côté des autres) essentiellement deux options:

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

et

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

Bien que les deux méthodes donnent le même résultat lorsque votre clé de tri est unique, n'oubliez pas que la méthode reverse va inverser l'ordre des clés identiques.

Exemple:

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

Bien que vous n’ayez souvent pas besoin de vous en soucier, vous le faites parfois. Pour éviter un tel comportement, vous pouvez introduire une deuxième clé de tri (qui doit bien sûr être unique, du moins pour tous les éléments ayant la même clé de tri):

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

En ce qui concerne la suite de références mentionnée ..., ces résultats sont également valables pour les tableaux triés. sort_by/reverse it est :)

Par exemple:

# 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

Et les résultats:

$: 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)
2

La solution simple ascendante décroissante et inversement est la suivante:

CHAÎNES

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

CHIFFRES

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

Pour ceux qui aiment mesurer la vitesse en 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

Et des résultats:

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