2010-11-29 27 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}