Ruby - 访问多维哈希并避免访问nil对象

Nob*_*ita 87 ruby hash conditional

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

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

my_hash['key1']['key2']['key3']
Run Code Online (Sandbox Code Playgroud)

如果key1中存在key1,key2和key3,这很好,但是如果,例如key1不存在呢?

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

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

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

这是否合适,还有其​​他Rubiest方式吗?

dbe*_*hur 170

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

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

my_hash.dig('key1', 'key2', 'key3')
Run Code Online (Sandbox Code Playgroud)

很多人坚持使用普通的红宝石并进行&&防护测试.

您也可以使用stdlib Hash #fetch :

my_hash.fetch('key1', {}).fetch('key2', {}).fetch('key3', nil)
Run Code Online (Sandbox Code Playgroud)

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

my_hash.try(:[], 'key1').try(:[], 'key2').try(:[], 'key3')
Run Code Online (Sandbox Code Playgroud)

其他人使用andand

myhash['key1'].andand['key2'].andand['key3']
Run Code Online (Sandbox Code Playgroud)

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

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

my_hash['key1']['key2']['key3']
Run Code Online (Sandbox Code Playgroud)

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

['key1','key2','key3'].reduce(my_hash) {|m,k| m && m[k] }
Run Code Online (Sandbox Code Playgroud)

或者使用嵌套查找方法扩展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'
Run Code Online (Sandbox Code Playgroud)

哦,我们怎么可能忘了,也许单子?

Maybe.new(my_hash)['key1']['key2']['key3']
Run Code Online (Sandbox Code Playgroud)

  • 你可以在Ruby 2.3之后使用`dig`方法进行哈希,http://ruby-doc.org/core-2.3.0_preview1/Hash.html#method-i-dig (7认同)
  • 我之前的评论中哈希上的安全导航器运算符的语法不正确。正确的语法是:`my_hash&.[]('key1')&.[]('key2')&.[]('key3')`。 (2认同)

Ser*_*sev 6

您也可以使用Object#和.

my_hash['key1'].andand['key2'].andand['key3']
Run Code Online (Sandbox Code Playgroud)


ing*_*ger 5

条件my_hash['key1'] && my_hash['key1']['key2']不觉得.

备择方案:

1)autovivification魔术.从那篇文章:

def autovivifying_hash
   Hash.new {|ht,k| ht[k] = autovivifying_hash}
end
Run Code Online (Sandbox Code Playgroud)

然后,用你的例子:

my_hash = autovivifying_hash     
my_hash['key1']['key2']['key3']
Run Code Online (Sandbox Code Playgroud)

它类似于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
Run Code Online (Sandbox Code Playgroud)

3)如果你觉得monadic你可以看看这个, 也许吧