2010-11-29 21 views
5

讓我們說我有每行號文件:如何組號碼分成不同的水桶紅寶石

0101 
1010 
1311 
0101 
1311 
431 
1010 
431 
420 

最後,我將不得不與每個號碼的出現次數的哈希,在這種情況下, :

{0101 => 2, 1010 => 2, 1311 => 2, 431 => 2, 420 => 1} 

我該怎麼做到這一點?

+1

我想我發現與不同的措詞對同一問題:) [計數紅寶石陣列重複的元素](http://stackoverflow.com/questions/569694/count-duplicate-elements-in-ruby-array ) – Matchu 2010-11-29 01:17:24

+0

:)多數民衆贊成在偉大的。謝謝。 – josh 2010-11-29 01:21:19

+0

僅供參考:如果碰巧是Rails,則可以使用Enumerable#group_by。請參閱http://api.rubyonrails.org/classes/Enumerable.html#method-i-group_by – 2010-11-29 16:36:53

回答

11

簡單的一行,給定一個數組items

items.inject(Hash.new(0)) {|hash, item| hash[item] += 1; hash} 

工作原理:

Hash.new(0)創建一個新的Hash,其中訪問未定義的鍵在一個數組返回0

inject(foo)迭代與給定的塊。對於第一次迭代,它通過foo,並在進一步的迭代中傳遞最後一次迭代的返回值。

另一種方式來寫這將是:

hash = Hash.new(0) 
items.each {|item| hash[item] += 1} 
2
ID = -> x { x } # Why is the identity function not in the core lib? 

f = <<-HERE 
    0101 
    1010 
    1311 
    0101 
    1311 
    431 
    1010 
    431 
    420 
HERE 

Hash[f.lines.map(&:to_i).group_by(&ID).map {|n, ns| [n, ns.size] }] 
# { 101 => 2, 1010 => 2, 1311 => 2, 431 => 2, 420 => 1 } 

您只需組使用Enumerable#group_by自己的數字,它給你像

{ 101 => [101, 101], 420 => [420] } 

然後你Enumerable#map值陣列到它們的長度,即[101, 101]變成2。然後使用Hash::[]將其轉換回Hash

但是,如果您願意使用第三方庫,它會變得更加微不足道,因爲如果您使用MultiSet數據結構,答案會自然而然地出現。 (A MultiSet就像一個Set,除了一個項目可以多次添加和MultiSet將保留的項目是如何經常地加入  –這是你想要什麼計數)。

require 'multiset' # Google for it, it's so old that it isn't available as a Gem 

Multiset[*f.lines.map(&:to_i)] 
# => #<Multiset:#2 101, #2 1010, #2 1311, #2 431, #1 420> 

是的,這是它。

這是關於使用正確數據結構的美妙之處:您的算法變得非常簡單。或者,在這種特殊情況下,算法消失

我已經寫了更多關於使用MultiSet局長在

4

這是基本相同,Chuck的,但是當你創建一個數組或哈希,「each_with_object」將使它比'inject'稍微簡單一些,因爲您不必在塊中編寫最終數組或散列。

items.each_with_object(Hash.new(0)) {|item, hash| hash[item] += 1}