创建一个带有默认键的新哈希表

11

我想创建一个哈希表,其中的索引来自于一个数组。

ary = ["a", "b", "c"]
h = Hash.new(ary.each{|a| h[a] = 0})

我的目标是从这样的哈希开始:

h = {"a"=>0, "b"=>0, "c"=>0}

这样,当哈希值改变时,我可以通过h.default将其重置。

不幸的是,我设置哈希值的方式不起作用...有什么想法吗?


你的代码毫无意义,显然你把它和哈希构造函数所接受的可选块混淆了:http://ruby-doc.org/core/classes/Hash.html#M000718。 - tokland
对于仍然困惑六年后的任何人,这个不起作用的原因是Hash.new()(以及Hash.new {})不是这样使用的;相反,它为尚未定义的键提供默认值Hash[](裸构造函数,而不是::new方法)允许您将现有数组转换为哈希表(虽然它也不会接受这样的参数)。 - Ryan Lue
7个回答

11
你应该先实例化你的哈希表 h,然后用数组的内容填充它:
h = {}    
ary = ["a", "b", "c"]
ary.each{|a| h[a] = 0}

为什么我尝试的那种方式无法将哈希声明与初始化值结合起来呢? - Jeremy Smith
我认为这是因为当你在each块中尝试访问变量h时,它仍然是nil。这就像说s = s + 4,这将给你一个NilClass错误。你在原始实现中遇到了什么错误? - McStretch

7

使用哈希的默认值功能

h = Hash.new(0)

h["a"]      # => 0

在这种方法中,密钥未设置。
h.key?("a") # => false

另一种方法是在访问时设置缺失的键。

h = Hash.new {|h, k| h[k] = 0}
h["a"]      # => 0
h.key?("a") # => true

即使采用这种方法,如果您之前没有访问过该键,像key?这样的操作也会失败。
h.key?("b") # => false
h["b"]      # => 0
h.key?("b") # => true

你可以始终采用暴力方法,这种方法的边界条件最少。
h = Hash.new.tap {|h| ["a", "b", "c"].each{|k| h[k] = 0}}
h.key?("b") # => true
h["b"]      # => 0

3
您可以像这样扩展列表为零初始化的值:
list = %w[ a b c ]

hash = Hash[list.collect { |i| [ i, 0 ] }]

你可以创建一个哈希表,对于任何给定的键,它都有一个默认值为0:
hash = Hash.new { |h, k| h[k] = 0 }

任何新引用的键都将被预初始化为默认值,这将避免初始化整个哈希表。

2
这可能不是最高效的方式,但我总是欣赏那些能揭示Ruby多功能性特点的一行代码。
h =  Hash[['a', 'b', 'c'].collect { |v|  [v, 0] }]

或者另一个一行代码实现相同功能的方法:
h = ['a', 'b', 'c'].inject({}) {|h, v| h[v] = 0; h }

顺便提一下,从性能角度来看,这些单行代码的运行速度约为以下方式的80%:

h = {}
ary = ['a','b','c']
ary.each { |a| h[a]=0 }

1
不需要使用旧的Hash[*(...)flatten]模式,已经有一段时间了。现在可以使用Hash[...]来处理键值对。 - tokland
另外,还可以尝试与McStretch的解决方案进行基准测试,我认为你会发现它并不值得为了省去一些打字而采用。 - jesse reiss
那是一定的。但是单行代码总是更有趣。 - Kelly
稍微优雅一点(我认为)的inject单行代码版本:array.inject({}) { |h, v| h.update(v => 0) } - jsanders

1
Rails 6 在 Enumerable 模块中新增了 index_with 方法。该方法可用于从带有默认值或获取值的枚举器创建哈希表。请注意保留 HTML 标签。
ary = %w[a b c]
hash = ary.index_with(0) # => {"a"=>0, "b"=>0, "c"=>0}

0

使用实际添加的键来创建哈希表的另一种方法

Hash[[:a, :b, :c].zip([])] # => {:a=>nil, :b=>nil, :c=>nil}

Hash[[:a, :b, :c].zip(Array.new(3, 0))] # => {:a=>0, :b=>0, :c=>0}

0
另一种选择是使用我喜欢的Enum#inject方法,因为它很干净。不过,我还没有与其他选项进行基准测试。
h = ary.inject({}) {|hash, key| hash[key] = 0; hash}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接