如何一次获取多个哈希值?
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
【讨论】:
以上是关于如何一次获取多个哈希值?的主要内容,如果未能解决你的问题,请参考以下文章