2012-02-28 26 views
29

Tôi có một loạt các từ và tôi muốn nhận được một băm, trong đó các khóa là các từ và các giá trị là số đếm từ.Mảng để Hash: các từ đếm

Có cách nào đẹp hơn sau đó của tôi:

result = Hash.new(0) 
words.each { |word| result[word] += 1 } 
return result 
+0

Bạn đang thực hiện khóa học Berkeley SaaS? – Gordon

+2

Có, tôi có một giải pháp, nhưng tìm kiếm các phiên bản tốt hơn. – demas

+1

nếu 'kết quả [từ]' không tồn tại, nó sẽ ném một ngoại lệ vì không có '+' cho nil. –

Trả lời

51

Bạn đã viết phương pháp bắt buộc chung và có lẽ đó là thực hiện nhanh hơn trong Ruby. Với một chút tái cấu trúc, bạn có thể viết một lớp lót:

wf = Hash.new(0).tap { |h| words.each { |word| h[word] += 1 } } 

Một cách tiếp cận bắt buộc sử dụng Enumerable#each_with_object:

wf = words.each_with_object(Hash.new(0)) { |word, acc| acc[word] += 1 } 

Một cách tiếp cận chức năng sử dụng trừu tượng hiện có:

wf = words.group_by { |w| w }.map { |w, ws| [w, ws.length] }.to_h 

Lưu ý rằng đây vẫn là O (n) trong thời gian, nhưng nó đi qua bộ sưu tập ba lần và tạo ra hai đối tượng trung gian trên đường đi.

Cuối cùng: tần số truy cập/biểu đồ là một trừu tượng phổ biến mà bạn sẽ tìm thấy trong một số thư viện như Facets: Enumerable#frequency.

require 'facets' 
wf = words.frequency 
+0

Có thể đơn giản là 'str.split (" ") .reduce (Hash.new (0)) {| h, w | đặt h [w] + = 1; h} '? –

+1

Một số thử nghiệm tốc độ pinch-of-muối, ruby ​​2.0.0p451 trên macbook chạy mavericks: Declarative: '100.times {words.inject (Hash.new 0) {| h, w | h [w] + = 1; h}} ': avg 1,17s. Bắt buộc: '100.times {hist = Hash.new 0; words.each {| w | hist [w] + = 1}} ': avg 1.09s. 'từ' là một mảng của 10k từ ngẫu nhiên, thế hệ của mảng một mình mất 0,2g avg. tức là Imperative nhanh hơn khoảng 9%. –

+0

Cảm ơn bạn đã lưu ý cuối cùng về Facets. Tôi đã thực hiện lại điều này nhiều lần ngay bây giờ, và các khía cạnh giúp tôi tránh được rắc rối khi làm lại hoặc bắt đầu lib chuẩn của riêng tôi. Đối với những người khác, bạn nên kiểm tra Facets, nó giống như một phần mở rộng của thư viện chuẩn của Ruby. –

7

Với inject:

str = 'I have array of words and I want to get a hash, where keys are words' 
result = str.split.inject(Hash.new(0)) { |h,v| h[v] += 1; h } 

=> {"I"=>2, "have"=>1, "array"=>1, "of"=>1, "words"=>2, "and"=>1, "want"=>1, "to"=>1, "get"=>1, "a"=>1, "hash,"=>1, "where"=>1, "keys"=>1, "are"=>1} 

Tôi không biết về hiệu quả.

+1

Theo doc của phương pháp khía cạnh được đăng bởi tokland, 'inject' là chậm hơn. – Baldrick

+1

Ngoài ra, nếu bạn sử dụng 'inject' và bạn cần trả về đối tượng ở cuối khối như trên ('; h'), bạn nên sử dụng 'each_with_object' để thay thế. – mfilej

2
irb(main):001:0> %w(foo bar foo bar).each_with_object(Hash.new(0)) { |w, m| m[w] += 1 } 
=> {"foo"=>2, "bar"=>2} 

như @mfilej nói

0

tôi đã làm một cái gì đó tương tự như câu trả lời ở trên nhưng hơi khác nhau. Hy vọng nó có thể giúp ai đó ..

arr = ['a','b','a'] 
hash = {} 

arr.uniq.each do |e| 
    hash[e] = arr.count(e) 
end 

puts hash 
+0

Tốc độ này chậm hơn khoảng 10 lần so với các giải pháp khác được cung cấp. – Sixty4Bit

Các vấn đề liên quan