How do I use the fetch method for nested hash?

后端 未结 7 1420
后悔当初
后悔当初 2021-02-19 14:45

I have the following hash:

hash = {\'name\' => { \'Mike\' => { \'age\' => 10, \'gender\' => \'m\' } } }

I can access the age by:

相关标签:
7条回答
  • 2021-02-19 15:19

    As of Ruby 2.3.0:

    You can also use &. called the "safe navigation operator" as: hash&.[]('name')&.[]('Mike')&.[]('age'). This one is perfectly safe.

    Using dig is not safe as hash.dig(:name, :Mike, :age) will fail if hash is nil.

    However you may combine the two as: hash&.dig(:name, :Mike, :age).

    So either of the following is safe to use:

    hash&.[]('name')&.[]('Mike')&.[]('age')

    hash&.dig(:name, :Mike, :age)

    0 讨论(0)
  • 2021-02-19 15:19

    If you don't want to monkey patch the standard Ruby class Hash use .fetch(x, {}) variant. So for the example above will look like that:

    hash.fetch('name', {}).fetch('Mike', {}).fetch('age')
    
    0 讨论(0)
  • 2021-02-19 15:21

    There is no built-in method that I know of. I have this in my current project

    class Hash
      def fetch_path(*parts)
        parts.reduce(self) do |memo, key|
          memo[key.to_s] if memo
        end
      end
    end
    
    # usage
    hash.fetch_path('name', 'Mike', 'age')
    

    You can easily modify it to use #fetch instead of #[] (if you so wish).

    0 讨论(0)
  • 2021-02-19 15:22

    if you can

    use:

    hash[["ayy","bee"]]
    

    instead of:

    hash["ayy"]["bee"]
    

    it'll save a lot of annoyances

    0 讨论(0)
  • 2021-02-19 15:27

    If your goal is to raise a KeyError when any of the intermediate keys are missing, then you need to write your own method. If instead you're using fetch to provide default values for missing keys, then you can circumvent the use of fetch by constructing the Hashes with a default values.

    hash = Hash.new { |h1, k1| h1[k1] = Hash.new { |h2, k2| h2[k2] = Hash.new { |h3, k3| } } }
    hash['name']['Mike']
    # {}
    hash['name']['Steve']['age'] = 20
    hash
    # {"name"=>{"Mike"=>{}, "Steve"=>{"age"=>20}}}
    

    This won't work for arbitrarily nested Hashes, you need to choose the maximum depth when you construct them.

    0 讨论(0)
  • 2021-02-19 15:30

    A version that uses a method instead of adding to the Hash class for others using Ruby 2.2 or lower.

    def dig(dict, *args)
      key = args.shift
      if args.empty?
        return dict[key]
      else
        dig(dict[key], *args)
      end
    end
    

    And so you can do:

    data = { a: 1, b: {c: 2}}
    dig(data, :a) == 1
    dig(data, :b, :c) == 2
    
    0 讨论(0)
提交回复
热议问题