从Ruby中的哈希列表创建嵌套哈希

我有一组类别及其值存储为哈希列表:

r = [{:A => :X}, {:A => :Y}, {:B => :X}, {:A => :X}, {:A => :Z}, {:A => :X}, {:A => :X}, {:B => :Z}, {:C => :X}, {:C => :Y}, {:B => :X}, {:C => :Y}, {:C => :Y}] 

我想得到每个值的计数加上它的类别作为这样的哈希:

 {:A => {:X => 4, :Y => 1, :Z => 1}, :B => {:X => 2, :Z => 1}, :C => {:X => 1, :Y => 3}} 

我怎样才能有效地做到这一点?

这是我到目前为止(它返回不一致的值):

 r.reduce(Hash.new(Hash.new(0))) do |memo, x| memo[x.keys.first][x.values.first] += 1 memo end 

我应该首先计算特定{:cat => :val}的所有实例的计数,然后创建哈希吗? 我应该给出一个不同的基本情况来减少和改变主体来检查nil案例(并且在零时分配零)而不是总是加1吗?

编辑:

我最终更改了我的代码并使用下面的方法来实现嵌套哈希的更简洁方法:

 r.map do |x| [x.keys.first, x.values.last] end.reduce({}) do |memo, x| memo[x.first] = Hash.new(0) if memo[x.first].nil? memo[x.first][x.last] += 1 memo end 

你的代码的问题是: 备忘录没有保留值。 在循环外部使用变量来保持该值是正常的:

 memo = Hash.new {|h,k| h[k] = Hash.new {|hh, kk| hh[kk] = 0 } } r.each do |x| memo[x.keys.first][x.values.first] += 1 end p memo 

而且,如下所示,直接嵌套在哈希中的哈希是不行的:

 # NOT RIGHT memo = Hash.new(Hash.new(0)) memo = Hash.new({}) 

以下是有关设置默认值问题的更多信息的链接: http : //www.themomorohoax.com/2008/12/31/why-setting-the-default-value-of-a-hash-to-be-a -hash -是-错

不确定“不一致的值”意味着什么,但你的问题是你注入的哈希不记得它的结果

 r.each_with_object(Hash.new { |h, k| h[k] = Hash.new 0 }) do |individual, consolidated| individual.each do |key, value| consolidated[key][value] += 1 end end 

但老实说,最好只去你制作这个数组的地方并将其改为聚合这样的值。

function方法使用一些方便的抽象 – 无需重新发明轮子 – 从方面 :

 require 'facets' r.map_by { |h| h.to_a }.mash { |k, vs| [k, vs.frequency] } #=> {:A=>{:X=>4, :Y=>1, :Z=>1}, :B=>{:X=>2, :Z=>1}, :C=>{:X=>1, :Y=>3}}