有人可以解释一下Hash#dig与Hash#fetch有什么区别

时间:2019-03-30 08:36:29

标签: ruby-on-rails ruby ruby-hash

我正在尝试在哈希中获取嵌套值。我已经尝试过使用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

期望的返回值为"Rome"。第二个表达式有效,但是我想知道它是否可以简化。

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

2 个答案:

答案 0 :(得分:3)

Hash#fetch与此处无关。这是因为当fetch仅具有单个参数时,fetchHash#[]相同。因此,让我们专注于dig

在Ruby v2.3中引入了三个dig方法家族:Hash#digArray#digOpenStruct#dig。关于这些方法的一个有趣的事情是它们彼此调用(但是在文档中甚至在示例中都没有解释)。在您遇到的问题中,我们可以这样写:

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

response是一个哈希,因此Hash#dig评估response[:results]。如果其值为nil,则表达式返回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因此在Array#dig上调用arr,获得哈希值

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

Array#dig然后在Hash#dig上调用h

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

很老套,g是一个哈希,Hash#digHash#dig上调用g

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

答案 1 :(得分:1)

Hash#dig

  

dig(key, ...) → object

     

通过在每个步骤调用dig来提取由键对象序列指定的嵌套值,如果中间步骤为nil,则返回nil

Hash#fetch

  

fetch(key [, default] ) → obj

     

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

     

从散列返回给定键的值。如果找不到密钥,则有几种选择:没有其他参数,它将引发KeyError异常;如果给出了 default ,则将返回该值;如果指定了可选代码块,则将运行该代码块并返回其结果。

您的示例与众不同:

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"