Arrays 如何使用Ruby 2.3中引入的数组#dig和哈希#dig?

Arrays 如何使用Ruby 2.3中引入的数组#dig和哈希#dig?,arrays,ruby,hash,Arrays,Ruby,Hash,Ruby 2.3在Array和Hash上引入了一种称为dig的新方法。我在博客文章中看到的关于新版本的例子都是精心设计和复杂的: # Hash#dig user = { user: { address: { street1: '123 Main street' } } } user.dig(:user, :address, :street1) # => '123 Main street' # Array#dig results = [[[1, 2,

Ruby 2.3在
Array
Hash
上引入了一种称为
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
解决:


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

在我们的例子中,由于
nil
引用导致的
NoMethodError
s是我们在生产环境中看到的最常见的错误

新的
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]
请注意,将在别处创建的符号列表传递到
散列#dig
中是非常简单的,而从这样的列表中重新创建后一个构造并不十分简单
Hash#dig
允许您轻松进行动态访问,而无需担心
nil
引用

显然,
Hash#dig
也要短得多


需要注意的一个重要点是,
Hash#dig
本身在任何一个键出现时都会返回
nil
,这可能会导致同一类错误,因此最好提供一个合理的默认值。(这种提供始终响应预期方法的对象的方式称为。)

同样,在您的示例中,一个空字符串或类似“N/A”的内容,取决于什么是合理的:

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

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

理论上,它节省了大量的代码,否则这些代码将在每个级别上检查是否存在另一个级别,否则您将面临不断出错的风险。实际上,您可能仍然需要大量此代码,因为在某些情况下,
dig
仍会产生错误(例如,如果链中的任何对象是未设置关键帧的对象)

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


要使
dig
避免这些错误,请尝试gem,我编写它是为了将
dig
包装起来,并在出现任何错误时强制它返回nil/default。

您刚刚解析了一些json,这是一个非常现实的示例…@Jesseilaff它们不一样<如果任何键不存在,代码>[]将失败并出现错误
dig
不会失败,它将返回
nil
。首先,如果您使用索引,并且在过程中丢失了一些内容,那么nil类不会出现方法错误。其次,使用
dig
方法进行动态调用更容易。我希望有一个
Hash#dig
这相当于多个#fetch。@Dogweather:您可以尝试在中提交功能请求,看看是否有人接收到它。:-)有时可以看到的其他习惯用法是
arr.fetch(:user,{}).fetch(:address,{})[:street1]
和Rails,
arr[:user]。try(:[],:address)。try(:[],:street1)
@Drenmi是否有可能的情况下仍然更倾向于使用旧的[:a][:b]语法而不是dig,如果我需要通过散列访问一系列密钥?@MartinVerdejo:我能想到的唯一原因是,如果你正在处理一个需要与早期rubies保持向后兼容性的代码库。