如何一次获取多个哈希值?

Posted

技术标签:

【中文标题】如何一次获取多个哈希值?【英文标题】:How do I fetch multiple hash values at once? 【发布时间】:2013-07-12 20:51:27 【问题描述】:

什么是更短的版本?:

from = hash.fetch(:from)
to = hash.fetch(:to)
name = hash.fetch(:name)
# etc

注意fetch,如果密钥不存在,我想引发错误。

它必须有更短的版本,例如:

from, to, name = hash.fetch(:from, :to, :name) # <-- imaginary won't work

如果需要,可以使用 ActiveSupport。

【问题讨论】:

一个重要且未问的问题是。您想将值从 hash 重新分配到 vars 的目的是什么? @MichaelSzyndel 我无法解析您上面的评论。 为什么要使用from = hash.fetch(:from); to = hash.fetch(:to);... 而不是hash[:from] 这是一个普遍的问题,有太多不同的用例需要提及,就像有时需要通过使用 fetch 而不是 [] 来避免静默失败一样,有时需要使用values_at 的获取版本。 【参考方案1】:

使用Hash的values_at方法:

from, to, name = hash.values_at(:from, :to, :name)

这将为哈希中不存在的任何键返回nil

【讨论】:

这不满足OP的要求。 @MichaelSzyndel 自己阅读问题。 没有这样的方法可以获取多个键并为丢失的键获取异常。原因之一可能是 - 当应该引发异常时 - 当键不存在时(它可能会为其他键留下未分配的变量)或在获取所有键之后?自己添加这样的检查很容易,我真的看不出有什么理由坚持在这里提出异常。 @MichaelSzyndel “为什么坚持在这里提出异常” - 因为密钥必须存在。 “当应该引发异常时 - 当键不存在时(它可能会为其他键留下未分配的变量)” - 是的,如果是正常的 fetch 也会留下未分配的变量。 是的,但为什么不直接使用raise MyException if hsh_values.include? nil【参考方案2】:

Ruby 2.3 终于引入了fetch_values 哈希方法,它直接实现了这一点:

a: 1, b: 2.fetch_values(:a, :b)
# => [1, 2]
a: 1, b: 2.fetch_values(:a, :c)
# => KeyError: key not found: :c

【讨论】:

【参考方案3】:
hash = from: :foo, to: :bar, name: :buz

[:from, :to, :name].map|sym| hash.fetch(sym)
# => [:foo, :bar, :buz]
[:frog, :to, :name].map|sym| hash.fetch(sym)
# => KeyError

【讨论】:

【参考方案4】:
my_array = from: 'Jamaica', to: 'St. Martin'.values_at(:from, :to, :name)
my_array.keys.any? |key| element.nil? && raise || my_array

这将引发您要求的错误

 my_array = from: 'Jamaica', to: 'St. Martin', name: 'George'.values_at(:from, :to, :name)
 my_array.keys.any? |key| element.nil? && raise || my_array

这将返回数组。

但是 OP 要求在丢失的密钥上失败...

class MissingKeyError < StandardError
end
my_hash = from: 'Jamaica', to: 'St. Martin', name: 'George'
my_array = my_hash.values_at(:from, :to, :name)
my_hash.keys.to_a == [:from, :to, :name] or raise MissingKeyError
my_hash = from: 'Jamaica', to: 'St. Martin'
my_array = my_hash.values_at(:from, :to, :name)
my_hash.keys.to_a == [:from, :to, :name] or raise MissingKeyError

【讨论】:

` my_array.any? |元素| element.nil? && raise` 将不正确,因为可能存在带有 nil 值的键。 你是对的。我检查了值而不是键。我将编辑密钥。 keys.to_a == [:from, :to, :name] 不正确。顺序在这里很重要,但哈希键没有特定的顺序,它至少应该是keys.to_a - [:from, :to, :name] == []。总的来说,付出的努力多于其价值。 如果你做减法并且键根本不存在,你仍然会得到一个空数组,所以这也不好。所以匹配一个空数组并不意味着密钥一定在那里。错过了。【参考方案5】:

您可以使用KeyError 对象的默认值初始化您的哈希。如果您尝试获取的密钥不存在,这将返回一个 KeyError 实例。然后,您需要做的就是检查它的(值的)类,如果它是 KeyError,则引发它。

hash = Hash.new(KeyError.new("key not found"))

让我们在这个哈希中添加一些数据

hash[:a], hash[:b], hash[:c] = "Foo", "Bar", nil

最后查看值,如果找不到键则报错

hash.values_at(:a,:b,:c,:d).each |v| raise v if v.class == KeyError

当且仅当 key 不存在时才会引发异常。如果您有一个带有nil 值的键,它不会抱怨。

【讨论】:

我无法控制哈希的创建方式。所以我必须重新创建/复制只是为了使用“特殊”值作为无键的标记。【参考方案6】:

我会做的最简单的事情是

from, to, name = [:from, :to, :name].map |key| hash.fetch(key)

或者,如果你想使用values_at,你可以使用带有默认值块的Hash

hash=Hash.new |h, k| raise KeyError.new("key not found: #k.inspect") 
# ... populate hash
from, to, name = hash.values_at(:from, :to, :name) # raises KeyError on missing key

或者,如果你愿意的话,猴子补丁Hash

class ::Hash
  def fetch_all(*args)
    args.map |key| fetch(key)
  end
end
from, to, name = hash.fetch_all :from, :to, :name

【讨论】:

这和我的有什么不同? @sawa 显然,它分配了结果 LOL :)【参考方案7】:

模式匹配是 Ruby 2.7 中的一项实验性功能。

hash =  from: 'me', to: 'you', name: 'experimental ruby' 

hash in  from:, to:, name: 

见https://rubyreferences.github.io/rubyref/language/pattern-matching.html

【讨论】:

以上是关于如何一次获取多个哈希值?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Perl 哈希表中存储多个值?

带有键值的哈希映射 - 如何使用 bigdecimal 键获取值? [关闭]

如何获取当前 .exe 的哈希值?

如何找到包含匹配值的哈希键

如何获取 SSL 固定的公钥哈希?

API的理解和使用——哈希类型的命令