【问题标题】:How to assign hash['a']['b']= 'c' if hash['a'] doesn't exist?如果 hash['a'] 不存在,如何分配 hash['a']['b']= 'c'?
【发布时间】:2011-08-18 05:21:46
【问题描述】:

有没有比这个更简单的方法

if hash.key?('a')
  hash['a']['b'] = 'c' 
else  
  hash['a'] = {}
  hash['a']['b'] = 'c' 
end

【问题讨论】:

  • 关键字:“ruby hash auto-vivification auto-vivifying”(让我们看看 SO 是如何将它们链接起来的——参见“相关”)
  • @pst:我认为存在编辑冲突 - 有人修正了一些错别字并删除了您的标签。

标签: ruby hash variable-assignment autovivification hash-of-hashes


【解决方案1】:

最简单的方法是 construct your Hash 使用块参数:

hash = Hash.new { |h, k| h[k] = { } }
hash['a']['b'] = 1
hash['a']['c'] = 1
hash['b']['c'] = 1
puts hash.inspect
# "{"a"=>{"b"=>1, "c"=>1}, "b"=>{"c"=>1}}"

new 的这个表单创建一个新的空哈希作为默认值。你不想要这个:

hash = Hash.new({ })

因为这将对所有默认条目使用完全相同哈希。

此外,正如 Phrogz 所说,您可以使用 default_proc 使自动激活的哈希自动激活:

hash = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) }

更新:我想我应该澄清我对Hash.new({ }) 的警告。当你这样说时:

h = Hash.new({ })

这很像这样说:

h = Hash.new
h.default = { }

然后,当您访问 h 以将某些内容指定为 h[:k][:m] = y 时,它的行为就像您这样做了:

if(h.has_key?(:k))
    h[:k][:m] = y
else
    h.default[:m] = y
end

然后,如果你 h[:k2][:n] = z,你最终会分配 h.default[:n] = z。注意h 仍然说h.has_key?(:k) 是假的。

但是,当你这样说时:

h = Hash.new(0)

一切都会好起来的,因为您永远不会在此处修改h[k],您只会从h 中读取一个值(必要时将使用默认值)或为h 分配一个新值。

【讨论】:

  • 如您所知,但可能要指出的是,它只会自动激活一层深度,而这样做:Hash.new{ |h,k| h[k] = Hash.new(&h.default_proc) } 是海龟一直向下。
  • @mu:很好的解释,但我不明白that will use the exact same hash for all default entries.。我们不希望默认条目相同吗?
  • @Radek:不,您不希望默认条目是相同的 object。如果他们使用的是同一个对象,那么您最终会得到 hash['a']hash['b'] 是同一个对象,甚至不会在 hash 中以您希望的方式出现。在irb 中尝试我的示例,但使用Hash.new({}) 构造函数,您会发现问题所在。 Hash.new(x) 表单仅在 x 类似于 Fixnum 时才有效。
  • @Radek:在使用Hash.new({}) 运行示例后,检查hash.inspecthash.default.inspecthash['a'].inspecthash['b'].inspect
  • @Radek:我添加了一个更新,更好地解释了为什么Hash.new({}) 是个坏主意。
【解决方案2】:

一个简单的,但哈希应该是一个有效的哈希对象

(hash["a"] ||= {})['b'] = "c"

【讨论】:

  • 与使用默认 proc 相比,这种方法具有很大的灵活性。要构造一个仅在k = 'a' 时将h[k] 设置为{} 的默认散列,可以编写Hash.new { |_,k| k=='a' ? {} : nil },这非常难看。另一方面,表达式中的任何或所有字符串都可以是变量,因此绝不是限制性的。另外,我听说过这种方法往往比使用默认 proc 更快。奇怪的是'b' 用单引号括起来,而...... :-)
【解决方案3】:

如果您按以下方式创建hash,并使用新的(相同的默认值)哈希的默认值:(感谢 Phrogz 的更正;我的语法错误)

hash = Hash.new{ |h,k| h[k] = Hash.new(&h.default_proc) }

那你就可以了

hash["a"]["b"] = "c"

无需任何额外代码。

【讨论】:

  • 嗯,你可以它,但你的hash 不会改变。我想你的意思是:hash = Hash.new{ |h,k| h[k] = Hash.new(&h.default_proc) }
【解决方案4】:

这里的问题: Is auto-initialization of multi-dimensional hash array possible in Ruby, as it is in PHP? 提供了一个非常有用的AutoHash 实现。

【讨论】:

    【解决方案5】:
    class NilClass
      def [](other)
        nil
      end
    end
    

    一旦你定义了它,一切都会自动运行。但请注意,从现在开始,nil 在用作散列时将表现为空散列。

    【讨论】:

      猜你喜欢
      • 2011-03-13
      • 1970-01-01
      • 1970-01-01
      • 2012-06-13
      • 2012-09-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多