Accessing elements of nested hashes in ruby [dupli

2019-01-06 12:41发布

I'm working a little utility written in ruby that makes extensive use of nested hashes. Currently, I'm checking access to nested hash elements as follows:

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

Is there a better way to do this? I'd like to be able to say:

value = structure[:a][:b]

And get nil if :a is not a key in structure, etc.

15条回答
混吃等死
2楼-- · 2019-01-06 13:33

Traditionally, you really had to do something like this:

structure[:a] && structure[:a][:b]

However, Ruby 2.3 added a feature that makes this way more graceful:

structure.dig :a, :b # nil if it misses anywhere along the way

There is a gem called ruby_dig that will back-patch this for you.

查看更多
兄弟一词,经得起流年.
3楼-- · 2019-01-06 13:33

I think one of the most readable solutions is using 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
查看更多
放我归山
4楼-- · 2019-01-06 13:34

Ruby 2.3.0 introduced a new method called dig on both Hash and Array that solves this problem entirely.

value = structure.dig(:a, :b)

It returns nil if the key is missing at any level.

If you are using a version of Ruby older than 2.3, you can use the ruby_dig gem or implement it yourself:

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
查看更多
▲ chillily
5楼-- · 2019-01-06 13:35
value = structure[:a][:b] rescue nil
查看更多
干净又极端
6楼-- · 2019-01-06 13:35

Not that I would do it, but you can Monkeypatch in NilClass#[]:

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

> structure[:x][:y]
NoMethodError: undefined method `[]' for nil:NilClass
        from (irb):2
        from C:/Ruby/bin/irb:12:in `<main>'

> class NilClass; def [](*a); end; end
#=> nil

> structure[:x][:y]
#=> nil

> structure[:a][:y]
#=> nil

> structure[:a][:b]
#=> "foo"

Go with @DigitalRoss's answer. Yes, it's more typing, but that's because it's safer.

查看更多
够拽才男人
7楼-- · 2019-01-06 13:42

I made rubygem for this. Try vine.

Install:

gem install vine

Usage:

hash.access("a.b.c")
查看更多
登录 后发表回答