sort_by sort descending attribute array ruby sorting

descending - ruby sort array by attribute



Ordenar una matriz en orden descendente en Ruby (7)

Tengo una serie de hashes como siguiente

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

Estoy tratando de ordenar la matriz anterior en orden descendente de acuerdo con el valor de :bar en cada hash.

Estoy usando sort_by como siguiente para ordenar la matriz anterior.

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

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

Una solución fue hacer lo siguiente:

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

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


Para aquellas personas que 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


Podrías hacerlo:

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


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


Respecto a la suite de referencia mencionada ... estos resultados también son válidos para arreglos ordenados. sort_by / reverse is :)

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)


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

#!/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)

Creo que es interesante que @ la sort_by{...}.reverse! de 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 toma más tiempo que en invertir una matriz completa en una sola pasada. No hay mucha diferencia, 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 un viejo MacBook Pro. Las 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 actualizada del 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 una Macbook Pro más reciente. Una vez más, 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)


Solo una cosa rápida, que denota la intención del orden descendente.

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

(Pensará de una manera mejor en el tiempo);)

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


Veo que tenemos (al lado de otras) 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 invertirá el orden de las claves 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}]

Mientras que a menudo no necesitas preocuparte por esto, a veces lo haces. Para evitar tal comportamiento, puede introducir una segunda clave de clasificación (que seguramente debe ser única al menos para todos los elementos 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}]