2010-10-07 5 views
41

J'ai un tableau RubyRuby: Comment grouper un tableau 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"] 

et je veux une liste avec les comptes comme celui-ci:

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

Donc, idéalement, je veux un hachage qui me donnera le compte et notez comment je Enter Sandman et enter sandman donc j'ai besoin de lui insensible à la casse. Je suis assez sûr que je peux traverser, mais y at-il une façon plus propre?

Répondre

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

Le groupe crée par une table de hachage à partir de la version capitalize d d'un nom d'album à un tableau contenant toutes les chaînes de caractères qui correspondent à list elle (par exemple "Enter sandman" => ["Enter Sandman", "Enter sandman"]). Le map remplace ensuite chaque tableau par sa longueur, de sorte que vous obtenez par exemple. ["Enter sandman", 2] pour "Enter sandman".

Si le résultat doit être un hachage, vous pouvez appeler le to_h sur le résultat ou l'entourer d'un Hash[ ].

+2

Au lieu de 'capitalize', il y a un' titlecase' snippet ici: http://snippets.dzone.com/posts/show/294 –

7

Une autre prise:

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} 

Comme je l'ai commenté, vous pouvez préférer titlecase

+5

Dans ce cas, vous n'avez pas besoin d'utiliser la forme de bloc de Hash.new. Donc vous pouvez juste faire 'h = Hash.new (0)'. – sepp2k

+0

Cette réponse a attiré mon attention car elle est simple, lisible et flexible. – thekingoftruth

5

Regroupement et tri d'un ensemble de données de taille inconnue dans Ruby devrait être un choix de dernier recours. C'est une corvée qu'il vaut mieux laisser à DB. Typiquement, des problèmes comme le vôtre sont résolus en utilisant une combinaison des clauses COUNT, GROUP BY, HAVING et ORDER BY. Heureusement, les rails fournit une méthode count pour de tels cas d'utilisation.

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} 
Questions connexes