在ruby中访问嵌套哈希的元素

Pau*_*rie 31 ruby hash hash-of-hashes

我正在使用ruby编写的一个小实用程序,它广泛使用嵌套哈希.目前,我正在检查对嵌套哈希元素的访问,如下所示:

structure = { :a => { :b => 'foo' }}

# I want structure[:a][:b]

value = nil

if structure.has_key?(:a) && structure[:a].has_key?(:b) then
  value = structure[:a][:b]
end
Run Code Online (Sandbox Code Playgroud)

有一个更好的方法吗?我想能够说:

value = structure[:a][:b]
Run Code Online (Sandbox Code Playgroud)

而得到nil,如果:一个是不是一个关键structure,等等.

Dig*_*oss 39

传统上,你真的必须做这样的事情:

structure[:a] && structure[:a][:b]
Run Code Online (Sandbox Code Playgroud)

但是,Ruby 2.3增加了一个使这种方式更优雅的功能:

structure.dig :a, :b # nil if it misses anywhere along the way
Run Code Online (Sandbox Code Playgroud)

有一个叫做的宝石Hash#dig会为你补贴.

  • 但是如果你开始做'h = Hash.new({})之类的事情; h [:a] [:b] = 1; h [:c] [:d] = 2`你陷入混乱之中. (4认同)
  • 我认为你应该删除关于顶级哈希的一点,因为这不会任意深入,所以`h [:foo] [:bar] [:jim]`仍然会爆炸. (2认同)

use*_*951 35

Ruby 2.3.0引入了一个名为dig on 的新方法Hash,Array它完全解决了这个问题.

value = structure.dig(:a, :b)
Run Code Online (Sandbox Code Playgroud)

nil如果在任何级别缺少密钥,它将返回.

如果您使用的是早于2.3的Ruby版本,您可以使用ruby_diggem或自己实现:

module RubyDig
  def dig(key, *rest)
    if value = (self[key] rescue nil)
      if rest.empty?
        value
      elsif value.respond_to?(:dig)
    value.dig(*rest)
      end
    end
  end
end

if RUBY_VERSION < '2.3'
  Array.send(:include, RubyDig)
  Hash.send(:include, RubyDig)
end
Run Code Online (Sandbox Code Playgroud)


Pau*_*rie 28

这些天我通常这样做的方式是:

h = Hash.new { |h,k| h[k] = {} }
Run Code Online (Sandbox Code Playgroud)

这将为您提供一个哈希,它创建一个新哈希作为缺失键的条目,但返回第二级键的nil:

h['foo'] -> {}
h['foo']['bar'] -> nil
Run Code Online (Sandbox Code Playgroud)

您可以嵌套此项以添加可以通过这种方式解决的多个图层:

h = Hash.new { |h, k| h[k] = Hash.new { |hh, kk| hh[kk] = {} } }

h['bar'] -> {}
h['tar']['zar'] -> {}
h['scar']['far']['mar'] -> nil
Run Code Online (Sandbox Code Playgroud)

您还可以使用以下default_proc方法无限链接:

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

h['bar'] -> {}
h['tar']['star']['par'] -> {}
Run Code Online (Sandbox Code Playgroud)

上面的代码创建了一个哈希,其默认proc创建一个具有相同默认proc的新哈希.因此,在查找未见密钥时创建的哈希值将具有相同的默认行为.

编辑:更多细节

Ruby哈希允许您控制在为新键进行查找时如何创建默认值.指定时,此行为将封装为Proc对象,并可通过default_procdefault_proc=方法访问.也可以通过传递一个块来指定默认的proc Hash.new.

让我们把这段代码打破一点.这不是惯用的红宝石,但更容易将其划分为多行:

1. recursive_hash = Hash.new do |h, k|
2.   h[k] = Hash.new(&h.default_proc)
3. end
Run Code Online (Sandbox Code Playgroud)

1行声明的变量recursive_hash是一个新的Hash,并开始一个块要recursive_hashdefault_proc.该块传递两个对象:h这是Hash正在执行键查找的实例,并且k正在查找该键.

第2行将哈希中的默认值设置为新Hash实例.此哈希的默认行为是通过Procdefault_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=>{}}
Run Code Online (Sandbox Code Playgroud)

recursive_hash[:foo]创建散列时,它default_procrecursive_hash's提供default_proc.这有两个影响:

  1. 默认行为recursive_hash[:foo]recursive_hash.相同.
  2. recursive_hash[:foo]s 创建的哈希的默认行为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=>{}}}}
Run Code Online (Sandbox Code Playgroud)

  • 保罗,请注意你可以简化`Hash.new {| h,k | h [k] = {}}`到`Hash.new({})`和`Hash.new {| h,k | h [k] = Hash.new {| hh,kk | hh [kk] = {}}}`到`Hash.new(Hash.new({}))`. (2认同)

Che*_*eng 14

我为此制作了rubygem.试试藤蔓.

安装:

gem install vine
Run Code Online (Sandbox Code Playgroud)

用法:

hash.access("a.b.c")
Run Code Online (Sandbox Code Playgroud)


Jav*_*mae 7

我认为最易读的解决方案之一就是使用Hashie:

require 'hashie'
myhash = Hashie::Mash.new({foo: {bar: "blah" }})

myhash.foo.bar
=> "blah"    

myhash.foo?
=> true

# use "underscore dot" for multi-level testing
myhash.foo_.bar?
=> true
myhash.foo_.huh_.what?
=> false
Run Code Online (Sandbox Code Playgroud)