2010-10-07 37 views
41

Tôi có một mảng của RubyRuby: Làm thế nào để nhóm một mảng 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"] 

và tôi muốn có một danh sách với số lượng như thế này:

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

Vì vậy, lý tưởng tôi muốn có một băm mà sẽ cung cấp cho tôi những đếm và nhận thấy làm thế nào tôi có Enter Sandmanenter sandman vì vậy tôi cần nó không phân biệt chữ hoa chữ thường. Tôi khá chắc chắn tôi có thể lặp qua nó nhưng có cách nào sạch hơn không?

Trả lời

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

Các nhóm bởi tạo một hash từ phiên bản capitalize d của một tên album để một mảng chứa tất cả các chuỗi trong list phù hợp với nó (ví dụ "Enter sandman" => ["Enter Sandman", "Enter sandman"]). Sau đó, map sẽ thay thế từng mảng bằng độ dài của nó, do đó, bạn có được ví dụ: ["Enter sandman", 2] cho "Enter sandman".

Nếu bạn cần kết quả là băm, bạn có thể gọi to_h trên kết quả hoặc quấn Hash[ ] xung quanh nó.

+2

Thay vì 'capitalize', có một' titlecase' đoạn mã ở đây: http://snippets.dzone.com/posts/show/294 –

7

Một mất:

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} 

Như tôi đã nhận xét, bạn có thể thích titlecase

+5

Trong trường hợp này bạn không cần phải sử dụng dạng khối của Hash.new. Vì vậy, bạn chỉ có thể làm 'h = Hash.new (0)'. – sepp2k

+0

Câu trả lời này khiến tôi chú ý vì nó đơn giản, dễ đọc và linh hoạt. – thekingoftruth

5

Phân nhóm và phân loại của một tập dữ liệu kích thước chưa biết trong Ruby phải là một lựa chọn phương sách cuối cùng. Đây là một công việc tốt nhất còn lại để DB. Thông thường các vấn đề như của bạn được giải quyết bằng cách sử dụng kết hợp các điều khoản COUNT, GROUP BY, HAVINGORDER BY. May mắn thay, đường ray cung cấp phương thức count cho các trường hợp sử dụng này.

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} 
Các vấn đề liên quan