可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a Ruby array
> 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"]
and I want a list with the counts like this:
{"Nothing Else Matters" => 1, "Enter Sandman" => 2, "Master of Puppets" => 3}
So ideally I want a hash that will give me the count and notice how I have Enter Sandman
and enter sandman
so I need it case insensitive. I am pretty sure I can loop through it but is there a cleaner way?
回答1:
list.group_by(&:capitalize).map {|k,v| [k, v.length]} #=> [["Master of puppets", 3], ["Enter sandman", 2], ["Nothing else matters", 1]]
The group by creates a hash from the capitalize
d version of an album name to an array containing all the strings in list
that match it (e.g. "Enter sandman" => ["Enter Sandman", "Enter sandman"]
). The map
then replaces each array with its length, so you get e.g. ["Enter sandman", 2]
for "Enter sandman"
.
If you need the result to be a hash, you can call to_h
on the result or wrap a Hash[ ]
around it.
回答2:
list.inject(Hash.new(0)){|h,k| k.downcase!; h[k.capitalize] += 1;h}
回答3:
Another take:
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}
As I commented, you might prefer titlecase
回答4:
Grouping and sorting of a data set of unknown size in Ruby should be a choice of last resort. This is a chore best left to DB. Typically problems like yours is solved using a combination of COUNT
, GROUP BY
, HAVING
and ORDER BY
clauses. Fortunately, rails provides a count
method for such use cases.
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