I have the following Ruby hash
a = {
7 => [1469, 2283],
5 => [1469, 5464],
3 => [7561],
6 => [7952, 8114],
4 => []
}
and would like to get the keys that a number shows up in as a value
b = {
1469 => [7,5],
2283 => [7],
5464 => [5]
...
}
How would I do this? I'm sure there's some super slick way of getting it done.
CodePudding user response:
Given:
a = {
7 => [1469, 2283],
5 => [1469, 5464],
3 => [7561],
6 => [7952, 8114],
4 => [1469, 2283],
2 => []
}
Use a default value of a new array in b
:
b=Hash.new {|hsh, key| hsh[key] = [] }
a.each{|k,v| v.each{|n| b[n] << k} }
Or include the object creation with each_with_object:
b=a.each_with_object(Hash.new {|h,k| h[k] = []}) { |(k,v), h|
v.each{ |e| h[e] << k } }
Result b
is:
{1469=>[7, 5, 4], 2283=>[7, 4], 5464=>[5], 7561=>[3], 7952=>[6], 8114=>[6]}
CodePudding user response:
Using #each_with_object
to iterate over the original hash and the values stored in each array and build up a new hash based on it.
a.each_with_object({}) do |(k, v), h|
v.each do |val|
h[val] ||= []
h[val] << k
end
end
Result:
{
1469 => [7, 5],
2283 => [7],
5464 => [5],
7561 => [3],
7952 => [6],
8114 => [6]
}
CodePudding user response:
@dawg's answer is the way I would go
a.each_with_object(Hash.new {|h,k| h[k] = []}) do |(k,v),obj|
v.each{|e| obj[e] << k}
end
But here are a few other options: (a lot of allocations though)
Array#product
and Hash#merge
a.each_with_object({}) do |(k,v),obj|
obj.merge!(v.product([[k]]).to_h) {|_,o,v| [*o,*v]}
end
#map
then #reduce
a.map {|k,v| v.product([[k]]).to_h }
.reduce {|memo, h| memo.merge(h) {|_,o,n| o.concat(n)}}
This works too (don't ask questions)
a.map {|k,v| v.each_with_object(k)}
.reduce(&: )
.group_by(&:shift)
.transform_values(&:flatten)