【问题标题】:Could someone explain me what is the difference between Hash#dig vs Hash#fetch有人可以解释一下 Hash#dig 与 Hash#fetch 之间有什么区别吗
【发布时间】:2019-08-21 02:45:31
【问题描述】:

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

【问题讨论】:

  • 在 Ruby 2.4.5 中我没有得到相同的结果。 response.dig(:results)[0].dig(:value).dig(:destination) 给出“罗马”,response.dig(:results)[0].dig(:value).fetch('destination') 提出 KeyError: key not found: "destination"
  • 我忘了提到我正在使用 ruby​​ 2.5.3p105(2018-10-18 修订版 65156)[x86_64-linux] 和 Rails 5.2.1.1
  • @DavidAldridge 我添加了一张图片,希望现在这个问题更能说明问题
  • 您的问题很简单,但我认为它被我很多无关的信息弄糊涂了,我认为这些信息是反对票和投票结束的原因。要突出显示文本中的代码,请用反引号括起来。要突出显示代码块,请将其缩进 4 个空格或选择它并单击 {} 按钮。在给出示例时,为所有输入分配一个变量(以便读者可以在答案和 cmets 中引用这些变量而无需定义它们)并始终说明所需或预期的返回值 ("Rome")。 (续...)
  • ... 最后,在大多数 SO 问题中,您应该说出您想要实现的目标(返回 "Rome"),而无需提及应该如何完成。然后显示您尝试过的方法,即使用digfetch。有些人可能会将您的问题视为XY problem 的陈述。 (也有例外,例如当您想使用正则表达式从字符串中提取信息时,尽管您可能会邀请其他不使用正则表达式的解决方案。)如果您不喜欢我的编辑,请随意更改它.这是你的问题!

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


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

【讨论】:

    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-02
      • 2011-12-31
      • 2014-07-26
      相关资源
      最近更新 更多