是否有一种方便的方法来在ruby中构建嵌套哈希

时间:2013-12-14 12:20:00

标签: ruby

我有几个变量,例如小时分钟< / strong>即可。我需要对嵌套哈希进行一些操作,使用hash[2012][12][12][4][3]这些值。当遇到新的日期时,我需要扩展哈希值。

我可以通过判断每个级别逐个来完成此操作,并创建一个哈希(如果不存在)。但我想知道有一种方便的方法。

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

2 个答案:

答案 0 :(得分:1)

我同意sawa的评论。这种方法可能会有许多其他问题。但是,你想要的是什么。

搜索“ruby哈希默认值”。

默认情况下,哈希条目的默认值为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

您必须小心不要使用shared-default-instance,并使用不会改变它的操作。你不能只是

h[:mom] << 'thing'

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

See here for a good explanation and proper usage examples

或更好:example of autovivifying hash

答案 1 :(得分:1)

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

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以限制对当前类的添加。将它放在要包含的模块中可能会很方便。