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

Posted

技术标签:

【中文标题】Ruby - 访问多维哈希并避免访问 nil 对象【英文标题】:Ruby - Access multidimensional hash and avoid access nil object [duplicate] 【发布时间】:2012-04-25 05:18:30 【问题描述】:

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

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

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

如果 key1、key2 和 key3 存在于散列中,这很好,但如果 key1 不存在怎么办?

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

到目前为止,我通过以下条件处理这个问题:

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

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

【问题讨论】:

接受的答案提到了所有可能的方法,除了 Ruby 2.3+ 的正确方法:ruby-doc.org/core-2.3.1/Hash.html#method-i-dig 【参考方案1】:

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

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

【讨论】:

你也可以试试 monadic gem,它有一个 Maybe(和其他 monads)来帮助处理异常 您对在声明末尾使用rescue nil有何想法? @jakeonrails rescue nil 几乎总是邪恶的。 1)它可以捕获并静默丢弃您不知道可能会引发的异常; 2) 异常是计算成本高的流控制——应该只将它们用于异常行为,而不是预期行为。 Ruby 2.3之后可以使用dig方法进行哈希,ruby-doc.org/core-2.3.0_preview1/Hash.html#method-i-dig 我之前的评论中安全导航运算符的哈希值语法不正确。正确的语法是:my_hash&.[]('key1')&.[]('key2')&.[]('key3').【参考方案2】:

您也可以使用Object#andand。

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

【讨论】:

【参考方案3】:

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

【讨论】:

以上是关于Ruby - 访问多维哈希并避免访问 nil 对象的主要内容,如果未能解决你的问题,请参考以下文章

使用点分路径键字符串访问 Ruby 哈希

使用点分路径键字符串访问 Ruby 哈希

访问和更新哈希值 Ruby

ruby 将嵌套数组转换为哈希

等效于 .try() 的哈希以避免 nil 上的“未定义方法”错误? [复制]

Ruby - 关键字参数 - 你能把所有的关键字参数当作一个哈希值吗?如何?