Vivek
Vivek

Reputation: 163

Group array of arrays and select some column in Ruby

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

Answers (3)

Cary Swoveland
Cary Swoveland

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

undur_gongor
undur_gongor

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

Santhosh
Santhosh

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

Related Questions