哈希中的哈希值,用于生成默认数组

时间:2014-11-11 22:43:18

标签: ruby hash

假设我有:

a = Hash.new
a['google.com'][:traffic] << 50
a['google.com'][:traffic] << 20
a['google.com'][:popular] << 1
a['google.com'][:popular] << 2
3a['yahoo.com'][:anythinghere] << 3

需要产生这样的东西:

a = { 'google.com' => {traffic: [50,20], popular: [1,2]}, 'yahoo.com' => { anythinghere: [3,4] } }

到目前为止,我已尝试过这种方式,希望能产生这样的结果:

a= Hash.new(Hash.new(Array.new))

例如,a['google.com']将生成新哈希,而a['google.com'][:anythinghere]将生成新数组。但是,当我尝试执行上述插入时,a为空?不知道发生了什么,我很确定我在这里缺少一些基本的东西。看看:

a = stats = Hash.new(Hash.new(Array.new))
a['google.com'][:traffic] << 5
a['google.com'][:traffic] << 6
p a['google.com'][:traffic] #=> [5,6]
p a['google.com'] #=> empty hash???
p a #=> empty hash???

3 个答案:

答案 0 :(得分:4)

您获得此意外行为的原因是,当您将默认值作为new的参数传递给哈希时,一个对象是默认值用于所有密钥。例如:

s = "but"
a = Hash.new(s)
a['x'].concat 't'
puts a['y']
# butt
puts s
# butt

这是有道理的,因为Ruby使用指针传递对象,因此无论何时获得默认值,它都是指向您传递的原始对象的指针。

要解决此问题,您可以在块中设置默认值。这样,Hash每次需要默认值时都必须重新评估该块:

a = Hash.new { "but" }
a['x'].concat 't'
puts a['x']
# but
puts a.size
# 0

接下来的问题是,当Ruby获得默认值时,它不会将其分配给任何键。这就是为什么在访问前一个示例中的密钥后哈希的大小仍为0的原因。这个问题也可以解决,因为Ruby将哈希本身和缺少的键提供给默认值块,所以我们可以在那里进行分配:

a = Hash.new { |hash, key| hash[key] = "but" }
a['x'].concat 't'
puts a['x']
# butt
puts a['y']
# but
puts a.size
# 2

答案 1 :(得分:1)

您可以使用以下行:Hash.new { |h, k| h[k] = Hash.new { |h2, k2| h2[k2] = [] } }

a = Hash.new { |h, k| h[k] = Hash.new { |h2, k2| h2[k2] = [] } }
a['google.com'][:traffic] << 50
a['google.com'][:traffic] << 20
a['google.com'][:popular] << 1
a['google.com'][:popular] << 2
a['yahoo.com'][:anythinghere] << 3
a
# => {"google.com"=>{:traffic=>[50, 20], :popular=>[1, 2]}, "yahoo.com"=>{:anythinghere=>[3]}}

我发现提供给Hash.new的块有点乱,所以它可以像这样清理:

def Hash.default_set(&block)
   Hash.new { |h, k| h[k] = block.call }
end
a = Hash.default_set { Hash.default_set { [] } }
# ...

答案 2 :(得分:0)

另一种方式:

<强>代码

def hashify(arr)
  arr.each_with_object({}) do |e,h|
    *hash_keys, array_key, value = e
    g = hash_keys.reduce(h) { |g,k| g[k] ||= {} }
    (g[array_key] ||= []) << value
  end
end  

<强>实施例

a = [['google.com', :traffic, 50],
     ['google.com', :traffic, 20],
     ['google.com', :popular, 1],
     ['google.com', :popular, 2],
     ['yahoo.com',  :anythinghere, 3],
     ['yahoo.com',  :anythinghere, 4]]

hashify(a)  
  #=> {"google.com"=>{:traffic=>[50, 20],
  #    :popular=>[1, 2]},
  #    "yahoo.com"=>{:anythinghere=>[3, 4]}}

a = [['google.com', :traffic, 50],
     ['google.com', :traffic1, :cat, :dog, 20],
     ['google.com', :traffic1, :cat, :dog, 30],
     ['google.com', :popular, 1],
     ['google.com', :popular, 2],
     ['yahoo.com',  :anythinghere, 3],
     ['yahoo.com',  :anythinghere, 4]]

hashify(a)  
  #=> {"google.com"=>{:traffic=>[50], :traffic1=>{:cat=>{:dog=>[20, 30]}},
  #    :popular=>[1, 2]},
  #    "yahoo.com"=>{:anythinghere=>[3, 4]}}