如何将数字分组到ruby中的不同桶中

假设我的每行都有一个带数字的文件:

0101 1010 1311 0101 1311 431 1010 431 420 

最后,我会得到一个散列,其中包含每个数字的出现次数,在这种情况下:

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

我该怎么做到这一点?

简单的单线程,给定一个数组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} 

这与Chuck的基本相同,但是当你创建一个数组或散列时,’each_with_object’将使它比’inject’稍微简单,因为你不必在块中写入最后的数组或散列。

 items.each_with_object(Hash.new(0)) {|item, hash| hash[item] += 1} 
 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自行对数字进行分组,它可以为您提供类似的function

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

然后你Enumerable#map将值数组Enumerable#map到它们的长度,即[101, 101]变为2 。 然后使用Hash::[]将其转换回Hash::[]

但是,如果您愿意使用第三方库,它会变得更加微不足道,因为如果您使用MultiSet数据结构,答案自然就会消失。 ( 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来解决这个问题的更多信息

  • 找到重复数字的重复。 (该post中的第一个示例与此处的第二个示例相同,该post中的第二个示例与@ Chuck在此处基本相同,第三个示例与此post中的第一个示例相同。)
  • 计算字符串中唯一字母的出现 (Ruby示例与此处相同,Scala和ECMAScript示例与@ Chuck相同,C#示例与此处的group_by示例相同。)
  • scala中的Bag / Multiset和一些multimap (再次与我的两个和Scala中的@ Chuck相同的三个例子。)