【问题标题】: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 的获取版本。

标签: ruby-on-rails ruby hash destructuring


【解决方案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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-02-16
        • 2016-07-22
        • 1970-01-01
        • 2013-01-09
        • 1970-01-01
        • 2011-04-16
        • 2015-10-17
        • 2012-07-01
        相关资源
        最近更新 更多