【问题标题】:is there a convenience way to build nested hash in ruby [duplicate]有没有一种方便的方法可以在 ruby​​ 中构建嵌套哈希 [重复]
【发布时间】:2014-01-02 04:57:47
【问题描述】:

我有几个变量,例如 yearmonthdayhourminute。我需要使用这些值对嵌套哈希执行一些操作,例如hash[2012][12][12][4][3]。遇到新日期时,我需要扩展哈希。

我可以通过一个一个判断每个级别来做到这一点,如果不存在则创建一个哈希。但我想知道有一种方便的方法可以做到这一点。

在 perl 中,我可以直接调用 $hash{$v1}{$v2}{$v3}..{$vN} = something,因为默认情况下可以创建未定义的哈希。

【问题讨论】:

  • 除非您需要访问中间哈希,否则嵌套哈希在这里是一个糟糕的设计。我会分配一个数组作为键,即hash[[2012, 12, 12, 4, 3]] = :foo
  • 或者使用Date 对象作为键。
  • @sawa 谢谢你的建议

标签: ruby


【解决方案1】:

我同意sawa 的评论。使用这种方法,您可能会遇到许多其他问题。但是,你想要的都是可能的。

搜索“ruby 哈希默认值”。

默认情况下,Hash entry的默认值为nil。你可以改变它,即:

h = Hash.new(5)

现在h 在询问不存在的密钥时将返回 5,而不是 nil。您可以命令它返回一个新的空数组,或者以类似的方式返回一个新的空哈希。

但是,要小心。通过所有条目很容易意外地共享默认实例。

h = Hash.new([]) // default value = Array object, let's name it X
one = h[:dad]  // returns THE SAME object X
two = h[:mom]  // returns THE SAME object X

您必须小心不要使用共享默认实例,并使用不会改变它的操作。你不能只是

h[:mom] << 'thing'

因为h[:brandnewone] 现在将返回带有“事物”的变异默认实例。

See here for a good explanation and proper usage examples

或者,更好的是:example of autovivifying hash

【讨论】:

  • 避免您提到的问题的一种常见方法是使用块:h = Hash.new{[]}。但是你的答案不一定清楚它是如何扩展到任意深度的。
  • 真的。我忘记了阻止选项。我终于找到了this answer about auto-vivification,它解释了如何使用该选项递归地产生新的哈希值。
【解决方案2】:

您可以添加一个辅助方法,您可能会发现它在其他情况下很有用:

def mfetch(hash, *keys)
  return nil if (keys.empty? || !hash[keys.first]) 
  return hash[keys.first] if keys.size == 1
  k = keys.shift
  raise ArgumentError, "Too many keys" unless hash[k].is_a? Hash
  return mfetch(hash[k], *keys)
end

h = {cat: {dog: {pig: 'oink'}}}    # => {:cat=>{:dog=>{:pig=>"oink"}}} 
mfetch(h, :cat, :dog, :pig)        # => "oink"
mfetch(h, :cat, :dog)              # => {:pig=>"oink"}  
mfetch(h, :cat)                    # => {:dog=>{:pig=>"oink"}} 
mfetch(h, :cow)                    # => nil 
mfetch(h, :cat, :cow)              # => nil 
mfetch(h, :cat, :dog, :cow)        # => nil 
mfetch(h, :cat, :dog, :pig, :cow)  # => ArgumentError: Too many keys

如果您愿意,也可以将该方法添加到 Hash 类中:

class Hash
  def mfetch(*keys)
    return nil if (keys.empty? || !hash[keys.first]) 
    return self[keys.first] if keys.size == 1
    k = keys.shift
    raise ArgumentError, "Too many keys" unless self[k].is_a? Hash
    return self[k].mfetch(*keys)
  end
end

h.mfetch(:cat, :dog, :pig)         # => "oink"

或者如果您使用的是 Ruby 2.0,请将 class Hash 替换为 refine Hash do 以限制添加到当前类。将其放入要包含的模块中可能会很方便。

【讨论】:

    猜你喜欢
    • 2011-07-22
    • 2013-07-28
    • 2020-06-19
    • 2011-09-19
    • 1970-01-01
    • 2015-08-14
    • 1970-01-01
    • 1970-01-01
    • 2014-12-06
    相关资源
    最近更新 更多