Search code examples
rubyhashdefinedhash-of-hashes

Nested hash defined?()


What's the most concise way to determine if @hash[:key1][:key2] is defined, that does not throw an error if @hash or @hash[:key1] are nil?

defined?(@hash[:key1][:key2]) returns True if @hash[:key1] exists (it does not determine whether :key2 is defined)


Solution

  • When using ActiveSupport (Rails) or Backports, you can use try:

    @hash[:key1].try(:fetch, :key2)
    

    You could even handle @hash being nil:

    @hash.try(:fetch, :key1).try(:fetch, :key2)
    

    If you want @hash to always return a hash for a missing key:

    @hash = Hash.new { |h,k| h[k] = {} }
    @hash[:foo] # => {}
    

    You could also define this recursive:

    def recursive_hash
      Hash.new { |h,k| h[k] = recursive_hash }
    end
    
    @hash = recursive_hash
    @hash[:foo][:bar][:blah] = 10
    @hash # => {:foo => {:bar => {:blah => 10}}}
    

    But to answer your question:

    module HasNestedKey
      Hash.send(:include, self)
      def has_nested_key?(*args)
        return false unless sub = self[args.shift]
        return true if args.empty?
        sub.respond_to?(:has_nested_key?) and sub.has_nested_key?(*args)
      end
    end
    
    @hash.has_nested_key? :key1, :key2