web-dev-qa-db-fra.com

Ruby: Comment grouper un tableau Ruby?

J'ai un tableau de rubis

> 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 ceci:

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

Donc, idéalement, je veux un hachage qui me donnera le compte et remarquera comment j'ai Enter Sandman et enter sandman donc j'en ai besoin de la casse. Je suis à peu près sûr de pouvoir le parcourir en boucle, mais existe-t-il un moyen plus propre?

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

Le groupe by crée un hachage à partir de la version capitalized d'un nom d'album dans un tableau contenant toutes les chaînes de list qui lui correspondent (par exemple "Enter sandman" => ["Enter Sandman", "Enter sandman"]). La map remplace ensuite chaque tableau par sa longueur. Vous obtenez par exemple, par exemple. ["Enter sandman", 2] pour "Enter sandman".

Si vous souhaitez que le résultat soit un hachage, vous pouvez appeler to_h sur le résultat ou envelopper un Hash[ ] autour de celui-ci.

81
sepp2k
list.inject(Hash.new(0)){|h,k| k.downcase!; h[k.capitalize] += 1;h}
11
ghostdog74

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 pourriez préférer titlecase

9
glenn jackman

Le regroupement et le tri d'un ensemble de données de taille inconnue dans Ruby doivent être un choix de dernier recours. C’est une tâche qu'il vaut mieux laisser à DB. Généralement, des problèmes comme le vôtre sont résolus à l'aide d'une combinaison de clauses COUNT, GROUP BY, HAVING et ORDER BY. Heureusement, 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
5
Harish Shetty

Réponse tardive mais nette que j'ai,

l = list.group_by(&:titleize)
l.merge(l) { |k,v| l[k] = v.count }

Remarque: si nous voulons des clés uniques, c'est-à-dire sans titleize, alors remplacez-les par itself

0
ray