Pregunta Ordenar una matriz en orden descendente en Ruby


Tengo una variedad de hashes como los siguientes

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

Estoy tratando de ordenar arriba de la matriz en orden descendente según el valor de :bar en cada hash.

estoy usando sort_by como sigue para ordenar arriba de la matriz.

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

Sin embargo, arriba ordena la matriz en orden ascendente. ¿Cómo lo hago ordenar en orden descendente?

Una solución era hacer lo siguiente:

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

Pero ese signo negativo no parece apropiado. ¿Alguna opinión?


225
2018-04-15 01:32


origen


Respuestas:


Siempre es esclarecedor hacer un punto de referencia en las diversas respuestas sugeridas. Esto es lo que descubrí:

#! / usr / bin / ruby

requiere 'punto de referencia'

ary = []
1000.tiempo {
  ary << {: bar => rand (1000)}
}

n = 500
Benchmark.bm (20) do | x |
  x.report ("ordenar") {n.times {ary.sort {| a, b | b [: bar] <=> a [: bar]}}}
  x.report ("orden inverso") {n.times {ary.sort {| a, b | a [: bar] <=> b [: bar]} .reverse}}
  x.report ("sort_by -a [: bar]") {n.times {ary.sort_by {| a | -un 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}}
fin

                          sistema de usuario total real
tipo 3.960000 0.010000 3.970000 (3.990886)
ordenar revertir 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)

Creo que es interesante que @ Pablo sort_by{...}.reverse! es el más rápido Antes de ejecutar la prueba, pensé que sería más lento que "-a[:bar]"pero negar el valor resulta tomar más tiempo de lo que invierte todo el conjunto en una sola pasada. No es muy diferente, pero cada pequeña aceleración ayuda.


Tenga en cuenta que estos resultados son diferentes en Ruby 1.9

Aquí están los resultados para Ruby 1.9.3p194 (2012-04-20 revisión 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)

Estos están en una vieja MacBook Pro. Máquinas más nuevas o más rápidas tendrán valores más bajos, pero las diferencias relativas se mantendrán.


Aquí hay una versión poco actualizada sobre hardware más nuevo y la versión 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)

Nuevos resultados ejecutando el código anterior usando Ruby 2.2.1 en un Macbook Pro más reciente. Nuevamente, los números exactos no son importantes, son sus relaciones:

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)

475
2018-04-16 06:23



Solo una cosa rápida, que denota el intento de orden descendente.

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

(Pensará en una mejor manera mientras tanto);)


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

81
2018-04-15 01:35



Podrías hacerlo:

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

51
2018-04-15 01:40



Qué pasa:

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

¡¡Funciona!!

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
2018-04-15 01:38



Con respecto a la suite de referencia mencionada ... estos resultados también se mantienen para las matrices ordenadas. sort_by / reverse es :)

P.ej:

# 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

Y los resultados:

$: 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
2017-08-27 18:29



Veo que tenemos (al lado de otros) básicamente dos opciones:

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

y

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

Si bien ambas formas le dan el mismo resultado cuando su clave de clasificación es única, tenga en cuenta que la reverse camino lo hará invierta el orden de las llaves que son iguales.

Ejemplo:

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

Si bien a menudo no necesita preocuparse por esto, a veces lo hace. Para evitar este tipo de comportamiento, puede introducir una segunda clave de clasificación (que seguramente debe ser única, al menos para todos los artículos que tienen la misma clave de clasificación):

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

2
2017-10-18 09:54



Para aquellos a quienes les gusta medir la velocidad 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

Y resultados:

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
2018-05-16 11:15