有人可以解释一下 Hash#dig 和 Hash#fetch 之间的区别吗

Could someone explain me what is the difference between Hash#dig vs Hash#fetch

我正在尝试获取散列中的嵌套值。我试过使用 Hash#fetchHash#dig 但我不明白应该如何组合它们。

我的哈希如下。

response = {
   "results":[
      {
         "type":"product_group",
         "value":{
            "destination":"Rome"
         }
      },
      {
         "type":"product_group",
         "value":{
            "destination":"Paris"
         }
      },
      {
         "type":"product_group",
         "value":{
            "destination":"Madrid"
         }
      }
   ]
}

我试过以下方法

response.dig(:results)[0].dig(:value).dig(:destination) #=> nil
response.dig(:results)[0].dig(:value).fetch('destination') #=> Rome

所需的 return 值为 "Rome"。第二个表达式有效,但我想知道是否可以简化它。

我正在使用 Ruby v2.5 和 Rails v5.2.1.1。

Hash#dig

dig(key, ...) → object

Extracts the nested value specified by the sequence of key objects by calling dig at each step, returning nil if any intermediate step is nil.

Hash#fetch

fetch(key [, default] ) → obj

fetch(key) {| key | block } → obj

Returns a value from the hash for the given key. If the key can't be found, there are several options: With no other arguments, it will raise a KeyError exception; if default is given, then that will be returned; if the optional code block is specified, then that will be run and its result returned.

看你的例子有什么不同:

response = {
  "results": [
    {
      "type": 'product_group',
      "value": {
        "destination": 'Rome'
      }
    },
    {
      "type": 'product_group',
      "value": {
        "destination": 'Paris'
      }
    },
    {
      "type": 'product_group',
      "value": {
        "destination": 'Madrid'
      }
    }
  ]
}

response[:results].first.dig(:value, :destination) #=> "Rome"
response[:results].first.fetch(:value).fetch(:destination) #=> "Rome"

Hash#fetch is not relevant here. That's because fetch is the same as Hash#[] 就像这里一样,fetch 只有一个参数。所以让我们专注于 dig.

Ruby v2.3 中引入了三个 dig 方法家族:Hash#dig, Array#dig and OpenStruct#dig。这些方法的一个有趣之处在于它们会相互调用(但这在文档中没有解释,甚至在示例中也没有)。在你的问题中我们可以这样写:

response.dig(:results, 0, :value, :destination)
  #=> "Rome" 

response 是一个散列,因此 Hash#dig 计算 response[:results]。如果它的值是 nil 那么表达式 returns nil。例如,

response.dig(:cat, 0, :value, :destination)
  #=> nil

其实response[:results]是一个数组:

arr = response[:results]
  #=> [{:type=>"product_group", :value=>{:destination=>"Rome"}},
  #    {:type=>"product_group", :value=>{:destination=>"Paris"}},
  #    {:type=>"product_group", :value=>{:destination=>"Madrid"}}]

Hash#dig 因此在 arr 上调用 Array#dig,获取哈希

h = arr.dig(0)
  #=> {:type=>"product_group", :value=>{:destination=>"Rome"}} 

Array#dig 然后在 h:

上调用 Hash#dig
g = h.dig(:value)
  #=> {:destination=>"Rome"}

最后,g 作为散列,Hash#digg 上调用 Hash#dig:

g.dig(:destination)
  #=> "Rome"

使用任何 dig 时都需要小心。假设

arr = [[1,2], [3,[4,5]]]

我们希望拉出现在被4占用的对象。我们可以这样写

arr[1][1][0]
  #=> 4

arr.dig(1,1,0)
  #=> 4

现在假设 arr 更改如下:

arr = [[1,2]]

然后

arr[1][1][0]
  #=> NoMethodError: undefined method `[]' for nil:NilClass

arr.dig(1,1,0)
  #=> nil

两者都表明我们的代码中存在错误,因此引发异常比返回 nil 更可取,这可能会在一段时间内被忽视。