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

时间:2010-11-29 01:12:59

标签: ruby

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

0101
1010
1311
0101
1311
431
1010
431
420

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

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

我怎样才能做到这一点?

3 个答案:

答案 0 :(得分: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}

答案 1 :(得分:4)

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

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

答案 2 :(得分: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数据结构,答案自然就会消失。 (MultiSetSet类似,不同之处在于可以多次添加项目,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来解决这个问题的详细信息