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

时间:2012-04-12 19:50:51

标签: ruby hash conditional

  

可能重复:
  Ruby: Nils in an IF statement
  Is there a clean way to avoid calling a method on nil in a nested params hash?

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

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

如果hash1中存在key1,key2和key3,这很好,但是如果key1不存在会怎么样?

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

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

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

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

3 个答案:

答案 0 :(得分:164)

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

如果您使用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']

有些人认为egocentric nils是个好主意(虽然如果他们发现你这样做,有人可能会追捕你并折磨你。)

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 monad?

Maybe.new(my_hash)['key1']['key2']['key3']

答案 1 :(得分:6)

您也可以使用Object#andand

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

答案 2 :(得分:5)

条件my_hash['key1'] && my_hash['key1']['key2']感觉不到DRY

备选方案:

1)autovivification魔法。从那篇文章:

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)如果你觉得monadic,你可以看看这个Maybe