设置ruby哈希默认为列表

我以为我明白了什么是默认的方法做一个哈希…

如果键不存在,请为其提供默认值:

irb(main):001:0> a = {} => {} irb(main):002:0> a.default = 4 => 4 irb(main):003:0> a[8] => 4 irb(main):004:0> a[9] += 1 => 5 irb(main):005:0> a => {9=>5} 

都好。

但是,如果我设置默认为一个空列表,或空哈希,我不明白这是所有的行为….

 irb(main):001:0> a = {} => {} irb(main):002:0> a.default = [] => [] irb(main):003:0> a[8] << 9 => [9] # great! irb(main):004:0> a => {} # ?! would have expected {8=>[9]} irb(main):005:0> a[8] => [9] # awesome! irb(main):006:0> a[9] => [9] # unawesome! shouldn't this be [] ?? 

我希望/期待与我使用|| =操作符相同的行为…

 irb(main):001:0> a = {} => {} irb(main):002:0> a[8] ||= [] => [] irb(main):003:0> a[8] << 9 => [9] irb(main):004:0> a => {8=>[9]} irb(main):005:0> a[9] => nil 

任何人都可以解释发生了什么事?

Hash.default用于设置查询不存在的键时返回的默认值。 集合中的条目不是为您创build的,只是因为查询它。

另外,你设置的default是一个对象的实例(在你的情况下是一个数组),所以当它返回时,它可以被操纵。

 a = {} a.default = [] # set default to a new empty Array a[8] << 9 # a[8] doesn't exist, so the Array instance is returned, and 9 appended to it a.default # => [9] a[9] # a[9] doesn't exist, so default is returned 

这是一个非常有用的习惯用法:

 (myhash[key] ||= []) << value 

它甚至可以嵌套:

 ((myhash[key1] ||= {})[key2] ||= []) << value 

另一种方法是做:

 myhash = Hash.new {|hash,key| hash[key] = []} 

但是这有一个重要的副作用,那就是询问一个关键字是否会创build一个has_key? 相当无用,所以我避免了这种方法。

我认为这是你正在寻找的行为。 这将自动初始化哈希中的任何新密钥到一个数组:

 irb(main):001:0> h = Hash.new{|h, k| h[k] = []} => {} irb(main):002:0> h[1] << "ABC" => ["ABC"] irb(main):003:0> h[3] => [] irb(main):004:0> h => {1=>["ABC"], 3=>[]} 

格伦·麦克唐纳说:

“另一种方法是做:

myhash = Hash.new {| hash,key | hash [key] = []}

但是这有一个重要的副作用,就是询问关键字是否会创buildhas_key? 相当无用,所以我避免了这种方法。“

这实际上并不是真的。

 irb(main):004:0> a = Hash.new {|hash,key| hash[key] = []} => {} irb(main):005:0> a.has_key?(:key) => false irb(main):006:0> a[:key] => [] irb(main):007:0> a.has_key?(:key) => true 

如我所料, 访问密钥将创build它。 仅仅问has_key? 才不是。

如果你真的想有一个无穷无尽的哈希:

 endless = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) } endless["deep"]["in"]["here"] = "hello" 

当然,正如Glenn指出的那样,如果你这样做,has_key? 失去它的意义,因为它将永远回报真实。 Thx为这个jbarnette。

 irb(main):002:0> a.default = [] => [] irb(main):003:0> a[8] << 9 => [9] # great! 

有了这个声明,你已经修改了默认值; 你还没有创build一个新的数组,并添加“9”。 在这一点上,如果你这样做,这是相同的:

 irb(main):002:0> a.default = [9] => [9] 

因此,你现在得到这个结果并不奇怪:

 irb(main):006:0> a[9] => [9] # unawesome! shouldn't this be [] ?? 

此外,“<<”将“9”添加到数组中; 它没有将它添加到散列,这解释了这一点:

 irb(main):004:0> a => {} # ?! would have expected {8=>[9]} 

而不是使用.default,你可能想在你的程序中做的是这样的:

 # Time to add a new entry to the hash table; this might be # the first entry for this key.. myhash[key] ||= [] myhash[key] << value 

我不知道这是否是你想要的,但是你可以这样做,当查询丢失的散列键时总是返回一个空数组。

 h = Hash.new { [] } h[:missing] => [] #But, you should never modify the empty array because it isn't stored anywhere #A new, empty array is returned every time h[:missing] << 'entry' h[:missing] => []