ruby 将 2 元素数组的数组转换为哈希,其中重复的键附加附加值
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9270972/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Convert array of 2-element arrays into a hash, where duplicate keys append additional values
提问by smallsense
For example
例如
Given an array:
给定一个数组:
array = [[:a,:b],[:a,:c],[:c,:b]]
Return the following hash:
返回以下哈希:
hash = { :a => [:b,:c] , :c => [:b] }
hash = Hash[array]overwrites previous associations, producing:
hash = Hash[array]覆盖以前的关联,产生:
hash = { :a => :c , :c => :b }
回答by Phrogz
Using functional baby steps:
使用功能性婴儿步骤:
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]}
Using imperative style programming:
使用命令式编程:
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]}
As an imperative one-liner:
作为必要的单行:
irb:13.0> h = Hash.new{ |h,k| h[k]=[] }.tap{ |h| array.each{ |k,v| h[k] << v } }
#=> {:a=>[:b, :c], :c=>[:b]}
Or using everyone's favorite inject:
或者使用大家最喜欢的inject:
irb:14.0> array.inject(Hash.new{ |h,k| h[k]=[] }){ |h,(k,v)| h[k] << v; h }
#=> {:a=>[:b, :c], :c=>[:b]}
If you really want to have single values not collided as an array, you can either un-array them as a post-processing step, or use a different hash accumulation strategy that only creates an array upon collision. Alternatively, wrap your head around this:
如果您真的希望单个值不会作为数组发生冲突,您可以将它们取消排列作为后处理步骤,或者使用不同的散列累积策略,仅在冲突时创建数组。或者,将您的头围绕在此:
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}
回答by Abdo
EDIT:In Ruby 2.1+, you can use Array#to_h
编辑:在 Ruby 2.1+ 中,您可以使用Array#to_h
pry(main)> [[:a,:b],[:a,:c],[:c,:b]].to_h
=> {:a=>:c, :c=>:b}
END EDIT
结束编辑
The public [] method on the Hash class accepts a key-value pair array and returns a hash with the first element of the array as key and the second as value.
Hash 类上的 public [] 方法接受一个键值对数组,并返回一个哈希,其中数组的第一个元素作为键,第二个元素作为值。
The last value in the key-value pair will be the actual value when there are key duplicates.
键值对中的最后一个值将是键重复时的实际值。
Hash[[[:a,:b],[:a,:c],[:c,:b]]]
=> {:a=>:c, :c=>:b}
This syntax is valid in 1.9.3+ ; I'm not sure about earlier Ruby versions (it's not valid in 1.8.7)
此语法在 1.9.3+ 中有效;我不确定早期的 Ruby 版本(它在 1.8.7 中无效)
ref: http://www.ruby-doc.org/core-2.1.0/Hash.html#method-c-5B-5D
参考:http: //www.ruby-doc.org/core-2.1.0/Hash.html#method-c-5B-5D
Another interesting way of doing it would be using the inject method: (obviously the method above is more succinct and recommended for this specific problem)
另一个有趣的方法是使用注入方法:(显然上面的方法更简洁,推荐用于这个特定问题)
[ [:a, :b], [:a, :c], [:c, :b] ].inject({}) { |memo, obj|
memo[obj.first] = obj.last
memo
}
=> {:a=>:c, :c=>:b}
inject iterates over the enumerable, your array in this case, starting with the injected parameter, in this case the empty hash {}.
inject 迭代可枚举,在这种情况下是您的数组,从注入的参数开始,在这种情况下是空哈希 {}。
For each object in the enumerable, the block is called with the variables memo and obj:
对于可枚举中的每个对象,使用变量 memo 和 obj 调用块:
obj is the current object in the array
memo is the value that has been returned by your block's last iteration (for the first iteration, it's what you inject)
obj 是数组中的当前对象
memo 是块的最后一次迭代返回的值(对于第一次迭代,它是您注入的)
回答by Ian
This can be done fairly succinctly using each_with_object.
这可以使用each_with_object.
array.each_with_object({}) { |(k, v), h| h[k] = (h[k] || []) + [v] }
Demonstrating in irb:
展示在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]}

