将2元素数组的数组转换为散列,其中重复键附加其他值
例如
给定一个数组:
array = [[:a,:b],[:a,:c],[:c,:b]]
返回以下哈希:
hash = { :a => [:b,:c] , :c => [:b] }
hash = Hash[array]
覆盖以前的关联,产生:
hash = { :a => :c , :c => :b }
使用function性婴儿步骤:
irb:01.0> array = [[:a,:b],[:a,:c],[:c,:b]] #=> [[:a, :b], [:a, :c], [:c, :b]] irb:02.0> array.group_by(&:first) #=> {:a=>[[:a, :b], [:a, :c]], :c=>[[:c, :b]]} irb:03.0> array.group_by(&:first).map{ |k,a| [k,a.map(&:last)] } #=> [[:a, [:b, :c]], [:c, [:b]]] irb:04.0> Hash[ array.group_by(&:first).map{ |k,a| [k,a.map(&:last)] } ] #=> {:a=>[:b, :c], :c=>[:b]}
使用命令式样式编程:
irb:10.0> h = Hash.new{ |h,k| h[k]=[] } #=> {} irb:11.0> array.each{ |k,v| h[k] << v } #=> [[:a, :b], [:a, :c], [:c, :b]] irb:12.0> h #=> {:a=>[:b, :c], :c=>[:b]}
作为必要的单线:
irb:13.0> h = Hash.new{ |h,k| h[k]=[] }.tap{ |h| array.each{ |k,v| h[k] << v } } #=> {:a=>[:b, :c], :c=>[:b]}
或者使用每个人最喜欢的inject
:
irb:14.0> array.inject(Hash.new{ |h,k| h[k]=[] }){ |h,(k,v)| h[k] << v; h } #=> {:a=>[:b, :c], :c=>[:b]}
如果您确实希望单个值不与数组冲突,则可以将它们作为后处理步骤取消排列,或者使用不同的哈希累积策略,该策略仅在碰撞时创建数组。 或者,绕着这个:
irb:17.0> hashes = array.map{ |pair| Hash[*pair] } # merge many mini hashes #=> [{:a=>:b}, {:a=>:c}, {:c=>:b}] irb:18.0> hashes.inject{ |h1,h2| h1.merge(h2){ |*a| a[1,2] } } #=> {:a=>[:b, :c], :c=>:b}
编辑:在Ruby 2.1+中,您可以使用Array#to_h
pry(main)> [[:a,:b],[:a,:c],[:c,:b]].to_h => {:a=>:c, :c=>:b}
结束编辑
Hash类上的public []方法接受键值对数组,并返回一个散列,其中数组的第一个元素为键,第二个元素为值。
当存在密钥重复时,键值对中的最后一个值将是实际值。
Hash[[[:a,:b],[:a,:c],[:c,:b]]] => {:a=>:c, :c=>:b}
此语法在1.9.3+中有效; 我不确定早期的Ruby版本(它在1.8.7中无效)
参考: http : //www.ruby-doc.org/core-2.1.0/Hash.html#method-c-5B-5D
另一种有趣的方法是使用注入方法:(显然上面的方法更简洁,并推荐用于此特定问题)
[ [:a, :b], [:a, :c], [:c, :b] ].inject({}) { |memo, obj| memo[obj.first] = obj.last memo } => {:a=>:c, :c=>:b}
在这种情况下,注入迭代遍历可枚举的数组,从注入的参数开始,在本例中为空哈希{}。
对于可枚举中的每个对象,使用变量memo和obj调用该块:
-
obj是数组中的当前对象
-
memo是块最后一次迭代返回的值(对于第一次迭代,它是你注入的)
这可以使用each_with_object
相当简洁地each_with_object
。
array.each_with_object({}) { |(k, v), h| h[k] = (h[k] || []) + [v] }
在irb
展示:
irb(main):002:0> array = [[:a,:b],[:a,:c],[:c,:b]] => [[:a, :b], [:a, :c], [:c, :b]] irb(main):003:0> array.each_with_object({}) { |(k, v), h| h[k] = (h[k] || []) + [v] } => {:a=>[:b, :c], :c=>[:b]}