我现在通常这样做:
h = Hash.new { |h,k| h[k] = {} }
这将为您提供一个哈希表,它会为缺失的键创建一个新的哈希表条目,但对于第二层键返回nil:
h['foo'] -> {}
h['foo']['bar'] -> nil
您可以嵌套这个标签来添加多个层级,可以通过以下方式进行访问:
h = Hash.new { |h, k| h[k] = Hash.new { |hh, kk| hh[kk] = {} } }
h['bar'] -> {}
h['tar']['zar'] -> {}
h['scar']['far']['mar'] -> nil
您也可以使用 default_proc
方法无限链接:
h = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) }
h['bar'] -> {}
h['tar']['star']['par'] -> {}
上述代码创建了一个哈希表,其默认过程会创建一个具有相同默认过程的新哈希表。因此,当出现未见过的键查找时创建的哈希表将具有相同的默认行为。
编辑:更多细节
Ruby哈希表允许您控制在查找新键时如何创建默认值。当指定时,这种行为被封装为一个 Proc
对象,并可通过 default_proc
和 default_proc=
方法访问。也可以通过向 Hash.new
传递块来指定默认过程。
让我们稍微分解一下这段代码。虽然这不是习惯用法的 Ruby,但是将其拆分成多行更容易理解:
1. recursive_hash = Hash.new do |h, k|
2. h[k] = Hash.new(&h.default_proc)
3. end
第一行声明一个变量
recursive_hash
为新的
Hash
,并开始一个块作为
recursive_hash
的
default_proc
。该块传递了两个对象:
h
,它是正在执行键查找的
Hash
实例,以及
k
,被查找的键。
第二行将哈希中的默认值设置为新的
Hash
实例。该哈希的默认行为由从正在发生查找的哈希的
default_proc
创建的
Proc
提供;即块本身定义的默认proc。
这是来自IRB会话的示例:
irb(main):011:0> recursive_hash = Hash.new do |h,k|
irb(main):012:1* h[k] = Hash.new(&h.default_proc)
irb(main):013:1> end
=> {}
irb(main):014:0> recursive_hash[:foo]
=> {}
irb(main):015:0> recursive_hash
=> {:foo=>{}}
当创建
recursive_hash[:foo]
的哈希时,它的
default_proc
是由
recursive_hash
的
default_proc
提供的。这有两个效果:
recursive_hash[:foo]
的默认行为与recursive_hash
相同。
- 由
recursive_hash[:foo]
的default_proc
创建的哈希的默认行为将与recursive_hash
相同。
因此,在IRB中继续执行,我们得到以下结果:
irb(main):016:0> recursive_hash[:foo][:bar]
=> {}
irb(main):017:0> recursive_hash
=> {:foo=>{:bar=>{}}}
irb(main):018:0> recursive_hash[:foo][:bar][:zap]
=> {}
irb(main):019:0> recursive_hash
=> {:foo=>{:bar=>{:zap=>{}}}}
Hash#dig
来解决这个问题。请参见我下面的答案。 - user513951Hash#dig
,我会跟随@PaulMorie的答案,将使用安全导航运算符.&
—— 就像在h&.fetch(:a,nil).&fetch(:b,nil)
中,盲目地遍历嵌套哈希或混合结构,并处理不存在(nil)中间键。这种结构应该被尽量避免。dig
和.&
方法都是在Ruby 2.3.0中引入的。 - Claudio Floreani