I have a data pair:
[{:mobile=>21, :web=>43},{:mobile=>23, :web=>543},{:mobile=>23, :web=>430},{:mobile=>34, :web=>13},{:mobile=>26, :web=>893}]
How can I make this into:
[{mobile: [21, 23, 34, 26]}, {web: [43, 543, 430, 13, 893]}
When I look at the provide scenario I see the following solution:
data = [{:mobile=>21, :web=>43},{:mobile=>23, :web=>543},{:mobile=>23, :web=>430},{:mobile=>34, :web=>13},{:mobile=>26, :web=>893}]
keys = [:mobile, :web]
result = keys.zip(data.map { |hash| hash.values_at(*keys) }.transpose).to_h
#=> {:mobile=>[21, 23, 23, 34, 26], :web=>[43, 543, 430, 13, 893]}
This first extracts the values of the keys from each hash, then transposes the the resulting array. This changes [[21, 43], [23, 543], [23, 430], ...]
into [[21, 23, 23, ...], [43, 543, 430, ...]]
. This result can be zipped back to the keys and converted into a hash.
To get rid of duplicates you could add .each(&:uniq!)
after the transpose
call, or map the collections to a set .map(&:to_set)
(you need to require 'set'
) if you don't mind the values being sets instead of arrays.
result = keys.zip(data.map { |hash| hash.values_at(*keys) }.transpose.each(&:uniq!)).to_h
#=> {:mobile=>[21, 23, 34, 26], :web=>[43, 543, 430, 13, 893]}
require 'set'
result = keys.zip(data.map { |hash| hash.values_at(*keys) }.transpose.map(&:to_set)).to_h
#=> {:mobile=>#<Set: {21, 23, 34, 26}>, :web=>#<Set: {43, 543, 430, 13, 893}>}
References:
Array#map
Hash#values_at
*
(in hash.values_at(*keys)
)Array#zip
Array#transpose
Array#to_h
Array#each
Array#uniq!
Enumerable#to_set