如何使用Ruby 2.3中引入的Array#dig和Hash #dig?

时间:2015-12-18 00:18:13

标签: arrays ruby hash

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

3 个答案:

答案 0 :(得分:58)

在我们的案例中,由NoMethodError引用引起的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) || ""

答案 1 :(得分:9)

一种方法是与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

答案 2 :(得分:0)

这对于通过深度嵌套的哈希/数组进行工作很有用,例如,这可能是您从API调用中得到的结果。

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

因此,您的问题实际上是真的有效-dig尚未看到我们可能期望的用法。例如,在这里对此进行评论:Why nobody speaks about dig

要使dig避免这些错误,请尝试使用KeyDial的gem,我将其写成dig,并在出现任何错误时强制使其返回nil / default。