2010-10-07 37 views
41

Tengo una matriz de RubyRuby: ¿Cómo agrupar una matriz de Ruby?

> list = Request.find_all_by_artist("Metallica").map(&:song) 
=> ["Nothing else Matters", "Enter sandman", "Enter Sandman", "Master of Puppets", "Master of Puppets", "Master of Puppets"] 

y quiero una lista con los conteos de esta manera:

{"Nothing Else Matters" => 1, 
"Enter Sandman" => 2, 
"Master of Puppets" => 3} 

De manera ideal, quiero un hash que me dará el conteo y notar cómo tengo Enter Sandman y enter sandman, así que lo necesito sin distinción de mayúsculas y minúsculas. Estoy bastante seguro de que puedo recorrerlo pero ¿hay una manera más limpia?

Respuesta

80
list.group_by(&:capitalize).map {|k,v| [k, v.length]} 
#=> [["Master of puppets", 3], ["Enter sandman", 2], ["Nothing else matters", 1]] 

El grupo por crea un hash de la versión capitalize d de un nombre de álbum a una matriz que contiene todas las cuerdas en list que coincidan con ella (por ejemplo "Enter sandman" => ["Enter Sandman", "Enter sandman"]). El map luego reemplaza cada arreglo por su longitud, por lo que obtienes p. Ej. ["Enter sandman", 2] para "Enter sandman".

Si necesita que el resultado sea un hash, puede llamar al to_h en el resultado o ajustar un Hash[ ] alrededor de él.

+2

en lugar de 'capitalize', hay un' titlecase' snippet aquí: http://snippets.dzone.com/posts/show/294 –

7

Otra toma:

h = Hash.new {|hash, key| hash[key] = 0} 
list.each {|song| h[song.downcase] += 1} 
p h # => {"nothing else matters"=>1, "enter sandman"=>2, "master of puppets"=>3} 

Como os comentaba, es posible que prefiera titlecase

+5

En este caso no es necesario utilizar el forma de bloque de Hash.new. Entonces puedes hacer 'h = Hash.new (0)'. – sepp2k

+0

Esta respuesta me llamó la atención porque es simple, legible y flexible. – thekingoftruth

5

Agrupación y clasificación de un conjunto de datos de tamaño desconocido en Ruby debería ser una opción de último recurso. Esta es una tarea que le queda mejor a DB. Normalmente, problemas como el tuyo se resuelven usando una combinación de COUNT, GROUP BY, HAVING y ORDER BY cláusulas. Afortunadamente, rails proporciona un método count para tales casos de uso.

song_counts= Request.count(
       :select => "LOWER(song) AS song" 
       :group => :song, :order=> :song, 
       :conditions => {:artist => "Metallica"}) 

song_counts.each do |song, count| 
    p "#{song.titleize} : #{count}" 
end 
11
list.inject(Hash.new(0)){|h,k| k.downcase!; h[k.capitalize] += 1;h} 
Cuestiones relacionadas