¿Cómo ordenar un Ruby Hash por valor numérico?

154

Tengo un contador hash que estoy tratando de ordenar por conteo. El problema con el que me encuentro es que la función Hash.sort predeterminada ordena los números como cadenas en lugar de por tamaño de número.

es decir, Hash dado:

metrics = {"sitea.com" => 745, "siteb.com" => 9, "sitec.com" => 10 }

Ejecutando este código:

metrics.sort {|a1,a2| a2[1]<=>a1[1]}

devolverá una matriz ordenada:

[ 'siteb.com', 9, 'sitea.com', 745, 'sitec.com', 10]

Aunque 745 es un número mayor que 9, 9 aparecerá primero en la lista. Cuando trato de mostrar quién tiene la cuenta más alta, esto me dificulta la vida. :)

¿Alguna idea sobre cómo ordenar un hash (o incluso una matriz) por tamaño de valor numérico?

Agradezco cualquier ayuda.

Dustin M.
fuente
¿Qué versión de rubí usas? el resultado de su clasificación es muy extraño
fl00r

Respuestas:

268

No tengo idea de cómo obtuviste tus resultados, ya que no se ordenaría por valor de cadena ... Debes invertir a1y a2en tu ejemplo

La mejor manera en cualquier caso (según Mladen) es:

metrics = {"sitea.com" => 745, "siteb.com" => 9, "sitec.com" => 10 }
metrics.sort_by {|_key, value| value}
  # ==> [["siteb.com", 9], ["sitec.com", 10], ["sitea.com", 745]]

Si necesita un hash como resultado, puede usar to_h(en Ruby 2.0+)

metrics.sort_by {|_key, value| value}.to_h
  # ==> {"siteb.com" => 9, "sitec.com" => 10, "sitea.com", 745}
Marc-André Lafortune
fuente
84
o simplementesort_by{|k,v| v}
Mladen Jablanović
Mi número estaba regresando como una cadena, eso lo solucionó ... Tenía a2 y a1 en ese orden porque quería que los resultados ordenaran los descendientes ... aunque gracias por sus comentarios.
Dustin M.
es por eso que amo a Ruby ... tal problema es dolor de cabeza en otros idiomas. tan simple
Hady Elsahar
18
@Elchin: puedes usarmetrics.sort_by{ |k, v| v }.reverse.to_h
Marc-André Lafortune
44
En realidad, incluso más simple como: hash.sort_by (&: last) con la misma advertencia sobre cómo obtener una matriz de pares frente a un Hash.
Gerry Gleason
84

Como el valor es la última entrada, puede hacer:

metrics.sort_by(&:last)
shock_one
fuente
44
¡Esto es asombroso! cualquier referencia para la pre-definición de &:last?
Tamer Shlash
sort_by(&:last)es efectivamente una abreviatura de sort_by {|x| x.last} stackoverflow.com/questions/1217088/…
Beni Cherniavsky-Paskin
14

Ya respondí pero aún así. Cambia tu código a:

metrics.sort {|a1,a2| a2[1].to_i <=> a1[1].to_i }

Convertido a cadenas en el camino o no, esto hará el trabajo.

dimitarvp
fuente
9

Ese no es el comportamiento que estoy viendo:

irb(main):001:0> metrics = {"sitea.com" => 745, "siteb.com" => 9, "sitec.com" =>
 10 }
=> {"siteb.com"=>9, "sitec.com"=>10, "sitea.com"=>745}
irb(main):002:0> metrics.sort {|a1,a2| a2[1]<=>a1[1]}
=> [["sitea.com", 745], ["sitec.com", 10], ["siteb.com", 9]]

¿Es posible que en algún lugar a lo largo de la línea sus números se conviertan en cadenas? ¿Hay más código que no estás publicando?

Jacob Mattison
fuente
Ahh a la derecha, parece que el resultado en mi código lo devolvió como una cadena. Tipos de datos molestos. :) A veces estoy demasiado cerca del problema. Gracias.
Dustin M.
1
Sip. Ocasionalmente escucho a alguien referirse a Ruby como "sin tipo". Oh, no, definitivamente está escrito. Simplemente no está estáticamente escrito. :)
Jacob Mattison