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

可能重复:
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']

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

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

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

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

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

【问题讨论】:

标签: ruby hash conditional


【解决方案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

    【讨论】:

      猜你喜欢
      • 2013-12-06
      • 2021-12-10
      • 2013-04-18
      • 2011-08-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-14
      • 1970-01-01
      相关资源
      最近更新 更多