2010-10-07 89 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}