3

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

4

2 回答 2

14

Hash#fetch在这里不相关。这是因为fetchHash #[]和这里一样,fetch只有一个参数。所以让我们专注于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#digon arr,获取散列

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

Array#dig然后调用Hash#digh

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

最后,g作为一个哈希,Hash#dig调用Hash#digon g

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

dig使用任何's时都需要小心。认为

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返回更可取,这可能会在一段时间内被忽视。

于 2019-03-30T17:38:14.740 回答
2

Hash#dig

dig(key, ...) → object

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

Hash#fetch

fetch(key [, default] ) → obj

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

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

您的示例有何不同:

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"

于 2019-03-30T10:21:14.443 回答