如何使用数组中的元素作为哈希表的键进行初始化?

14
如何使用以下数组中的键初始化哈希表?
keys = [ 'a' , 'b' , 'c' ]

期望的哈希值h应为:

puts h 
# { 'a' => nil , 'b' => nil , 'c' => nil }

6个回答

16

我们使用Enumerable#each_with_objectHash :: []

 keys = [ 'a' , 'b' , 'c' ]
 Hash[keys.each_with_object(nil).to_a]
 # => {"a"=>nil, "b"=>nil, "c"=>nil}

或者使用Array#product

keys = [ 'a' , 'b' , 'c' ]
Hash[keys.product([nil])]
# => {"a"=>nil, "b"=>nil, "c"=>nil}

出现以下错误 - '>> attr = Hash[keys.each_with_object(nil)] ArgumentError: odd number of arguments for Hash from (irb):29:in []' from (irb):29 from C:/RailsInstaller/Ruby1.9.3/bin/irb:12:in <main>' >>' - user3206440
1
@user3206440 尝试使用 Hash[keys.each_with_object(nil).to_a] - Arup Rakshit
那个方法是 Hash.[],不是 Kernel#Hash,它们是完全不同的方法。 - toro2k
将键值初始化为空数组怎么样?就像以下代码一样:puts h #{'a' => [] , 'b' => [] , 'c' => []} - user3206440
1
Hash[ keys.product([ [] ]) ] 不会像你想的那样工作!它会将哈希表的每个成员初始化为相同的数组值。向其中一个数组添加成员会将相同的成员添加到所有数组中。 - smilingfrog

10
使用新的 (Ruby 2.1) to_h 方法:
keys.each_with_object(nil).to_h

我仍在使用Ruby2.0.0版本... 真丢人 :) - Arup Rakshit
这个想法似乎很棒。你能再解释一下每个_with_object如何与to_h一起工作吗?我尝试查看文档,但无法很好地理解它。 - Sam Kah Chiin

6

使用Array#zip的另一种替代方法:

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

更新:根据Arup Rakshit的建议,这里提供了所提议解决方案之间的性能比较:

require 'fruity'

ary = *(1..10_000)

compare do
  each_with_object {  ary.each_with_object(nil).to_a  }
  product          {  ary.product([nil])  }
  zip              {  ary.zip([])  }
  map              {  ary.map { |k| [k, nil] }  }
end

结果如下:
Running each test once. Test will take about 1 second.
zip is faster than product by 19.999999999999996% ± 1.0%
product is faster than each_with_object by 30.000000000000004% ± 1.0%
each_with_object is similar to map

创建一个基准报告。您最擅长这个。让我们看看哪个更快。。 :) - Arup Rakshit
请问您能解释一下吗? - toro2k
3
@ArupRakshit zip速度更快,但是使用each_with_index更加清晰和灵活(使用zip无法生成像{'a' => [], 'b' => [], ...}这样的哈希表)。在我看来,最好的折中方案是使用product - toro2k
它不是each_with_index,而是each_with_object - Arup Rakshit
很高兴看到您正在使用我的 fruity gem :-) 顺便说一下,您不需要使用这些前导 _。嗯,得修复这些 .99999% 的问题... - Marc-André Lafortune
1
顺便说一下,我刚刚在查看您的 compare_block 和 NamedBlockCollector 代码时,发现它是 Ruby 中最美丽的东西之一! - Abdo

1
=> keys = [ 'a' , 'b' , 'c' ]
=> Hash[keys.map { |x, z| [x, z] }]
# {"a"=>nil, "b"=>nil, "c"=>nil}

=> Hash[keys.map { |x| [x, nil] }]
# {"a"=>nil, "b"=>nil, "c"=>nil}

=> Hash[keys.map { |x, _| [x, _] }]
# {"a"=>nil, "b"=>nil, "c"=>nil}

为什么不直接使用 keys.map { |k| [k, nil] } 呢? - toro2k
返回仅翻译的文本:keys.map { |k, _| [k, _] } - Roman Kiselenko

0

我认为你应该尝试:

x = {}
keys.map { |k, _| x[k.to_s] = _ }

1
不需要使用 keys.map { |k, _| x[k.to_s] = _ },而是 keys.each { |k, _| x[k.to_s] = _ } 就足够了。 - Arup Rakshit

0
如果您需要使用特定内容初始化哈希表,您也可以使用 Array#zipArray#cycle
Hash[keys.zip([''].cycle)]
# => {"a"=>"", "b"=>"", "c"=>""}

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