【问题标题】:How do I use Array#dig and Hash#dig introduced in Ruby 2.3?如何使用 Ruby 2.3 中引入的 Array#dig 和 Hash#dig?
【发布时间】:2016-03-24 15:12:18
【问题描述】:

Ruby 2.3 在ArrayHash 上引入了一个名为dig 的新方法。我在博客文章中看到的有关新版本的示例都是人为的和令人费解的:

# Hash#dig
user = {
  user: {
    address: {
      street1: '123 Main street'
    }
  }
}

user.dig(:user, :address, :street1) # => '123 Main street'

# Array#dig
results = [[[1, 2, 3]]]
results.dig(0, 0, 0) # => 1

我没有使用三重嵌套平面数组。有什么实际的例子来说明这将如何有用?

更新

事实证明,这些方法解决了最常见的 Ruby 问题之一。下面的问题大约有 20 个重复,所有这些问题都通过使用 dig 解决:

How to avoid NoMethodError for missing elements in nested hashes, without repeated nil checks?

Ruby Style: How to check whether a nested hash element exists

【问题讨论】:

  • 你刚刚解析了一些json是一个很现实的例子……
  • 首先,如果您使用索引并且沿途缺少某些东西,您将不会收到 nil 类的方法错误。其次,使用dig方法更容易进行动态调用。

标签: arrays ruby hash splat


【解决方案1】:

在我们的例子中,NoMethodErrors 由于 nil 引用是迄今为止我们在生产环境中看到的最常见的错误。

新的Hash#dig 允许您在访问嵌套元素时省略nil 检查。由于哈希最适用于数据结构未知或不稳定的情况,因此对此有官方支持非常有意义。

让我们举个例子。以下:

user.dig(:user, :address, :street1)

等同于:

user[:user][:address][:street1]

如果user[:user]user[:user][:address]nil,这将导致运行时错误。

而是等价于下面的,也就是现在的成语:

user[:user] && user[:user][:address] && user[:user][:address][:street1]

请注意,将在别处创建的符号列表传递给Hash#dig 是多么简单,而从这样的列表重新创建后一个构造并不是很简单。 Hash#dig 允许您轻松进行动态访问,而不必担心 nil 引用。

显然Hash#dig 也短了很多。


需要注意的重要一点是,Hash#dig 本身会返回 nil,如果其中任何一个键被证明是,这可能会导致同一类错误,因此它可以是提供一个合理的默认值的好主意。 (这种提供始终响应预期方法的对象的方式称为Null Object Pattern。)

同样,在您的示例中,一个空字符串或类似“N/A”之类的东西,取决于什么是有意义的:

user.dig(:user, :address, :street1) || ""

【讨论】:

  • 我希望有一个 Hash#dig! 可以做多个 #fetch 的等效操作。
  • @Dogweather:您可以尝试在issue tracker 中提交功能请求,看看是否有人接听。 :-)
  • 有时看到的替代习语是arr.fetch(:user, {}).fetch(:address, {})[:street1],对于Rails,arr[:user].try(:[], :address).try(:[], :street1)
  • @Drenmi 是否有可能使用旧的 [:a][:b] 语法而不是 dig 语法,因为我需要访问散列中的键链?跨度>
  • @MartinVerdejo:我能想到的唯一原因是,如果您正在处理需要保持与早期 rubies 向后兼容的代码库。
【解决方案2】:

一种方法是结合从某个未知文档模型中读取的 splat 运算符。

some_json = JSON.parse( '{"people": {"me": 6, ... } ...}' )
# => "{"people" => {"me" => 6, ... }, ... }
a_bunch_of_args = response.data[:query]
# => ["people", "me"]
some_json.dig(*a_bunch_of_args)
# => 6

【讨论】:

    【解决方案3】:

    它对于处理深度嵌套的哈希/数组很有用,例如,这可能是您从 API 调用中得到的结果。

    理论上它可以节省大量代码,否则这些代码会在每个级别检查是否存在另一个级别,否则您将面临不断出错的风险。 在实践中你可能仍然需要很多这样的代码,因为dig 在某些情况下仍然会产生错误(例如,如果链中的任何东西都是非键控对象。)

    正是由于这个原因,您的问题实际上是有效的 - dig 没有看到我们可能期望的用法。此处对此进行了评论,例如:Why nobody speaks about dig

    要使dig 避免这些错误,请尝试KeyDial gem,我写它是为了环绕dig 并在出现任何错误时强制它返回nil/default。

    【讨论】:

      猜你喜欢
      • 2019-10-31
      • 1970-01-01
      • 1970-01-01
      • 2016-04-09
      • 1970-01-01
      • 2011-07-11
      • 2013-06-25
      • 2016-04-09
      • 1970-01-01
      相关资源
      最近更新 更多