【发布时间】: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
确定@hash[:key1][:key2] 是否已定义的最简洁方法是什么,如果@hash 或@hash[:key1] 为nil,则不会引发错误?
defined?(@hash[:key1][:key2]) 如果存在@hash[:key1],则返回True(不判断是否定义了:key2)
【问题讨论】:
标签: ruby hash defined hash-of-hashes
我刚刚发现的另一种选择是使用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。
【讨论】:
也许我遗漏了一些东西,但如果你只关心简洁......为什么不呢:
@hash && @hash[:key1] && @hash[:key1][:key2]
或者如果你想保存几个字符
@hash && (h = @hash[:key1]) && h[:key2]
如果其中任何部分失败,则返回nil,否则返回与:key2 或true 关联的值。
即使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)`
使用 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
【讨论】:
如果您不关心区分不存在的 @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)
【讨论】:
使用哈希#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)
【讨论】:
@hash[:key1].has_key? :key2
【讨论】:
NoMethodError: undefined method has_key?对于 nil:NilClass` 如果 @hash 没有 :key1 的值
fetch 是我在回答中建议的方法之一。见下文(或以上取决于 SO 的随机化器!)