Reputation: 163
I have this array of arrays:
[[F1,State1,Blah1],[F2,State1,Blah2],[F1,State2,Blah3]]
Whats a neat ruby way to convert the above to:
{State1=>[F1,F2],State2=>[F1]}
Upvotes: 1
Views: 427
Reputation: 110755
One more way is to use the form of Hash#update (aka merge!
) that uses a block to determine the values of keys that are present In both hashes being merged:
arr = [['F1','State1','Blah1'],['F2','State1','Blah2'],['F1','State2','Blah3']]
arr.each_with_object({}) { |(v,k,_),h| h.update(k=>[v]) { |_,ov,nv| ov+nv } }
#=> { "State1"=>["F1","F2"],"State2"=>["F1"] }
Upvotes: 1
Reputation: 15954
A one-pass solution with inject
:
array.inject({}) { | a, (v, k, _) | a[k] ||= []; a.update(k => a[k] << v) }
Or a bit cleaner:
array.inject(Hash.new { | h, k | h[k] = [] }) { | a, (v, k, _) | a.update(k => a[k] << v) }
This is the functional way of writing:
result = {}
array.each do | v, k, _ |
result[k] = [] unless result.has_key? k
result[k] << v
end
If you encounter performance issues, the update
should be avoided:
array.inject({}) { | a, (v, k, _) | a[k] ||= []; a[k] << v; a }
For completeness, there is also each_with_object
which is a bit more suitable here:
array.each_with_object({}) { | (v, k, _), a | a[k] ||= []; a[k] << v }
Upvotes: 2
Reputation: 29174
arr = [['F1','State1','Blah1'],['F2','State1','Blah2'],['F1','State2','Blah3']]
arr.group_by{|a| a[1]}.map {|k,v| [k, v.map(&:first)] }.to_h
# => {"State1"=>["F1", "F2"], "State2"=>["F1"]}
Upvotes: 4