【问题标题】:Nested hash defined?() [duplicate]嵌套哈希定义?()[重复]
【发布时间】:2011-03-22 07:46:58
【问题描述】:

确定@hash[:key1][:key2] 是否已定义的最简洁方法是什么,如果@hash@hash[:key1] 为nil,则不会引发错误?

defined?(@hash[:key1][:key2]) 如果存在@hash[:key1],则返回True(不判断是否定义了:key2

【问题讨论】:

    标签: ruby hash defined hash-of-hashes


    【解决方案1】:

    我刚刚发现的另一种选择是使用seek 方法扩展Hash。技术来自Corey O'Daniel

    把它放在初始化器中:

    class Hash
      def seek(*_keys_)
        last_level    = self
        sought_value  = nil
    
        _keys_.each_with_index do |_key_, _idx_|
          if last_level.is_a?(Hash) && last_level.has_key?(_key_)
            if _idx_ + 1 == _keys_.length
              sought_value = last_level[_key_]
            else                   
              last_level = last_level[_key_]
            end
          else 
            break
          end
        end
    
        sought_value
      end 
    end
    

    然后只需调用:

    @key_i_need = @hash.seek :one, :two, :three
    

    你会得到这个值,如果它不存在,你会得到 nil。

    【讨论】:

      【解决方案2】:

      也许我遗漏了一些东西,但如果你只关心简洁......为什么不呢:

      @hash && @hash[:key1] && @hash[:key1][:key2]
      

      或者如果你想保存几个字符

      @hash && (h = @hash[:key1]) && h[:key2]
      

      如果其中任何部分失败,则返回nil,否则返回与:key2true 关联的值。

      即使defined? 不存在,defined? 也返回 true 的原因是因为它只是检查您所引用的对象是否存在,在这种情况下是方法 [],它是方法 @ 的别名987654329@ 确实存在于哈希 @hash[:key1] 但如果返回 nil,则 nil 上没有 fetch 方法,它将返回 nil。话虽如此,如果您必须深入到 n 到嵌入式哈希中,那么在某些时候调用会变得更有效率:

      defined?(@hash[:key1][:key2][:key3]) && @hash[:key1][:key2][:key3]
      

      【讨论】:

      • x = {a:{b:{c:3}}} defined?(x[:z]) # => "method"
      • @hash = {:key1 => true} @hash && @hash[:key1] && @hash[:key1][:key2] # => NoMethodError (undefined method []' for true:TrueClass)`
      【解决方案3】:

      使用 ActiveSupport (Rails) 或 Backports 时,您可以使用try

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

      你甚至可以将@hash 处理成nil

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

      如果您希望 @hash 始终为丢失的键返回哈希:

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

      你也可以定义这个递归:

      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}}}
      

      但要回答你的问题:

      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
      

      【讨论】:

        【解决方案4】:

        如果您不关心区分不存在的 @hash[:key1][:key2](在 3 个级别中的任何一个级别)和 @hash[:key1][:key2] == nil,这很干净,适用于任何深度:

        [:key1,:key2].inject(hash){|h,k| h && h[k]}
        

        如果您希望 nil 被视为存在,请改用它:

        (hash[:key1].has_key?(:key2) rescue false)
        

        【讨论】:

        • 对正常程序流程滥用异常是一种代码异味,afaik。
        【解决方案5】:

        使用哈希#fetch

        您可以使用Hash#fetch 方法,默认为{},这样即使第一级密钥不存在,也可以安全地调用has_key?。例如

        !hash.nil? && hash.fetch(key1, {}).has_key?(key2)
        

        替代方案

        您也可以使用条件运算符,例如

        !hash.nil? && (hash.has_key?(key1) ? hash[key1].has_key?(key2) : false)
        

        即如果 hash 没有密钥 key1 则只需返回 false 而无需查找第二级密钥。如果确实有key1,则返回检查key1's 值是否为key2 的结果。

        另外,如果您想在调用 hash[key1]'s 值之前检查它是否具有 has_key? 方法:

        !hash.nil? && (hash.has_key?(key1) ? hash[key1].respond_to?(:has_key?) &&
           hash[key1].has_key?(key2) : false)
        

        【讨论】:

          【解决方案6】:
          @hash[:key1].has_key? :key2
          

          【讨论】:

          • 这会引发NoMethodError: undefined method has_key?对于 nil:NilClass` 如果 @hash 没有 :key1 的值
          • @Wayne 是的,fetch 是我在回答中建议的方法之一。见下文(或以上取决于 SO 的随机化器!)
          • @mikej,你是对的。对不起,我是多余的。我已经删除了我的评论,因为它不太有用。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-09-08
          • 1970-01-01
          • 2013-07-06
          • 1970-01-01
          相关资源
          最近更新 更多