简体   繁体   English

如何在访问哈希内部的哈希时处理潜在的错误?

[英]How to deal with potential errors while accessing a hash inside a hash?

Sometimes while dealing with API responses, I'll end up writing something like: 有时在处理API响应时,我最终会写一些类似于:

what_i_need = response["key"]["another key"]["another key 2"]

The problem with that is, it'll throw an error if, say, "another key" is missing. 问题是,如果缺少"another key" ,它会抛出一个错误。 I don't like that. 我不喜欢那样。 I'd be a lot happier if what_i_need turned up a nil if something along the process broke. 我会很多快乐,如果what_i_need变成了一个nil ,如果沿着过程的东西坏了。

Is there a more elegant solution than: 是否有比以下更优雅的解决方案:

what_i_need = nil
begin
  what_i_need = response["key"]["another key"]["another key 2"]
rescue Exception => e
end

I also thought about monkey patching NilClass you try to access nil["something"] it would return nil , but I'm not sure if that's the best way to go about it either of if it's possible even. 我也考虑过猴子修补NilClass,你试图访问nil["something"]它会返回nil ,但我不确定这是否是最好的方式来解决它,如果它甚至可能。

Use Hash#fetch with default value. 使用带有默认值的Hash #fetch

h = {:a => 2}
h.fetch(:b,"not present")
# => "not present"
h.fetch(:a,"not present")
# => 2

Without default value it will throw KeyError . 如果没有默认值,它将抛出KeyError

h = {:a => 2}
h.fetch(:b)
# ~> -:2:in `fetch': key not found: :b (KeyError)

But with nested Hash like your one you can use : 但是对于你可以使用的嵌套Hash你可以使用:

h = {:a => {:b => 3}}
val = h[:a][:b] rescue nil # => 3
val = h[:a][:c] rescue nil # => nil
val = h[:c][:b] rescue nil # => nil

Ruby 2.0有NilClass#to_h

what_i_need = response["key"].to_h["another key"].to_h["another key 2"]

Taking some inspiration from Objective-C's key-value coding system, you can do this with a lightweight DSL to walk a series of keys in an arbitrarily-nested data structure: 从Objective-C的键值编码系统中汲取灵感,您可以使用轻量级DSL在一个任意嵌套的数据结构中移动一系列键:

module KeyValue
  class << self
    def lookup(obj, *path)
      path.inject(obj, &:[]) rescue nil
    end
  end
end

h = { a: { b: { c: 42, d: [ 1, 2 ] } }, e: "test"}

KeyValue.lookup(h, :a, :b, :d, 1) # => 2
KeyValue.lookup(h, :a, :x)        # => nil

Or if you just want the one-liner: 或者,如果你只想要一个班轮:

(["key", "another key", "another key 2"].inject(h, &:[]) rescue nil)

To expand on @Priti's answer a bit, you can use a chain of Hash#fetch instead of Hash#[] to return empty hashes till you get to the last in the chain, and then return nil : 为了扩展@ Priti的答案 ,你可以使用一串 Hash#fetch而不是Hash#[]来返回空哈希,直到你到达链中的最后一个,然后返回nil

what_i_need = response.fetch('key', {})
                      .fetch('another key', {})
                      .fetch('another key 2', nil)

or rely on the KeyError exception being raised (maybe not the best, as exceptions as control flow should be avoided): 或者依赖于引发的KeyError异常(可能不是最好的,因为应该避免控制流的异常):

what_i_need = begin
                response.fetch('key').fetch('another key').fetch('another key 2')
              rescue KeyError
                nil
              end

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM