2010-10-07 85 views
41

我有一个Ruby阵列Ruby:如何分组一个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"] 

,我想通过这样的计数的列表:

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

所以最好我想一个散列,这将使我的计数,注意我怎么会有Enter Sandmanenter sandman,所以我需要它不区分大小写。我很确定我可以通过它循环,但有一个更清洁的方式?

回答

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

由群组创建从相册名的capitalize d版本的散列以包含所有字符串list在于与之匹配(例如"Enter sandman" => ["Enter Sandman", "Enter sandman"])的阵列。 map然后用它的长度替换每个数组,所以你得到例如["Enter sandman", 2]"Enter sandman"

如果您需要将结果作为散列,您可以对结果调用to_h或在其周围包装Hash[ ]

+2

相反capitalize'的',还有一个'titlecase'片断这里:http://snippets.dzone.com/posts/show/294 – 2010-10-07 19:37:18

7

另取:

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} 

正如我评论,你可能更喜欢titlecase

+5

在这种情况下,你不需要使用块形式的Hash.new。所以你可以做'h = Hash.new(0)'。 – sepp2k 2010-10-07 19:47:58

+0

这个答案引起了我的注意,因为它很简单,可读,灵活。 – thekingoftruth 2013-09-13 19:49:13

5

分组和红宝石未知大小的数据集的排序应该是最后不得已的选择。这是最好留给DB的一件苦差事。通常使用COUNT,GROUP BY,HAVINGORDER BY条款的组合解决像您这样的问题。幸运的是,rails为这种用例提供​​了一个count方法。

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}