90

可能重复:
Ruby:IF 语句中的 Nils
是否有一种干净的方法可以避免在嵌套的参数哈希中调用 nil 上的方法?

假设我尝试访问这样的哈希:

my_hash['key1']['key2']['key3']

如果 key1、key2 和 key3 存在于 hash(es) 中,这很好,但如果 key1 不存在怎么办?

然后我会得到NoMethodError: undefined method [] for nil:NilClass. 没有人喜欢那样。

到目前为止,我处理这个有条件的,如:

if my_hash['key1'] && my_hash['key1']['key2']...

这合适吗,还有其他 Rubiest 方法吗?

4

3 回答 3

185

有很多方法可以解决这个问题。

如果你使用 Ruby 2.3 或更高版本,你可以使用dig

my_hash.dig('key1', 'key2', 'key3')

很多人坚持使用纯红宝石并链接&&警卫测试。

您也可以使用 stdlib Hash#fetch

my_hash.fetch('key1', {}).fetch('key2', {}).fetch('key3', nil)

有些人喜欢链接 ActiveSupport 的#try方法。

my_hash.try(:[], 'key1').try(:[], 'key2').try(:[], 'key3')

其他人使用andand

myhash['key1'].andand['key2'].andand['key3']

有些人认为以自我为中心的 nil是个好主意(尽管如果他们发现你这样做,有人可能会追捕你并折磨你)。

class NilClass
  def method_missing(*args); nil; end
end

my_hash['key1']['key2']['key3']

您可以使用Enumerable#reduce(或别名注入)。

['key1','key2','key3'].reduce(my_hash) {|m,k| m && m[k] }

或者可能使用嵌套查找方法扩展 Hash 或只是您的目标哈希对象

module NestedHashLookup
  def nest *keys
    keys.reduce(self) {|m,k| m && m[k] }
  end
end

my_hash.extend(NestedHashLookup)
my_hash.nest 'key1', 'key2', 'key3'

哦,我们怎么能忘记可能的单子呢?

Maybe.new(my_hash)['key1']['key2']['key3']
于 2012-04-12T20:36:34.263 回答
6

您也可以使用Object#andand

my_hash['key1'].andand['key2'].andand['key3']
于 2012-04-12T20:24:15.920 回答
5

条件my_hash['key1'] && my_hash['key1']['key2']不感觉干燥

备择方案:

1)自动复活魔法。从那个帖子:

def autovivifying_hash
   Hash.new {|ht,k| ht[k] = autovivifying_hash}
end

然后,以您的示例:

my_hash = autovivifying_hash     
my_hash['key1']['key2']['key3']

它类似于 Hash.fetch 方法,两者都使用新的哈希作为默认值,但这会将细节转移到创建时间。诚然,这有点作弊:它永远不会返回 'nil' 只是一个空散列,它是动态创建的。根据您的用例,这可能是浪费。

2)通过查找机制抽象出数据结构,并在幕后处理未找到的情况。一个简单的例子:

def lookup(model, key, *rest) 
    v = model[key]
    if rest.empty?
       v
    else
       v && lookup(v, *rest)
    end
end
#####

lookup(my_hash, 'key1', 'key2', 'key3')
=> nil or value

3)如果你觉得单子,你可以看看这个, 也许

于 2012-04-12T19:58:42.963 回答