Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby array to hash

Tags:

ruby

I have the following array:

myarray = [
  ['usa','primary', 'john'],
  ['france','primary', 'lira'],
  ['usa', 'secondary', 'steve'],
  ['germany', 'primary', 'jeff'],
  ['france', 'secondary', 'ben']
]

I want to convert it to an array of hash like:

[
  {:country => 'usa', :primary => 'john', :secondary => 'steve'},
  {:country => 'france', :primary => 'lira', :secondary => 'ben'},
  {:country => 'germany', :primary => 'jeff', :secondary => ''}
]

I can do it by looping through the array and putting the values into a hash for "primary". How can I add "secondary" to the existing hash that may already be inserted into the array?

like image 836
JVK Avatar asked Oct 18 '12 23:10

JVK


People also ask

How do you turn an array into a hash in Ruby?

The to_h method is defined in the array class. It works to convert an array to a hash in the form of key-value pairs. The method converts each nested array into key-value pairs. The method also accepts a block.

How do you create a hash in Ruby?

In Ruby you can create a Hash by assigning a key to a value with => , separate these key/value pairs with commas, and enclose the whole thing with curly braces.

What does TO_H do in Ruby?

Ruby | Array to_h() function Array#to_h() : to_h() is a Array class method which returns the result of interpreting ary as an array of [key, value] pairs. Return: the result of interpreting ary as an array of [key, value] pairs.

What does .select do in Ruby?

Array#select() : select() is a Array class method which returns a new array containing all elements of array for which the given block returns a true value. Return: A new array containing all elements of array for which the given block returns a true value.


2 Answers

With a bit of Higher order programming:

myarray.reduce({}) do |accu, (country, func, name)| 
  accu[country] ||= {}
  accu[country][func.to_sym] = name
  accu 
end.map{|k, h| h[:country] = k; h}

Explanation:

Reduce will take an accumulator, in this case we start with an empty hash and go through the array. We match the triples in the array to the variables country, func, name. Since we want to group things by country we create that as our first Hash key. It should contain a hash so we make sure that is corresponds to an array with accu[country] ||= {}.

Then we add our key value pair converting func to a symbol. Finally we return our modified accumulator that will be passed to the next iteration.

This will return a data structure like this:

{"usa"=>{:primary=>"john", :secondary=>"steve"}, 
 "france"=>{:primary=>"lira", :secondary=>"ben"}, 
 "germany"=>{:primary=>"jeff"}} 

Now we need to transform it into a array of hashes rather than a big hash. We do this by calling map on it and in the process we add country as a key to the hash.

Now one thing that the algorithm above doesn't do is to check for missing values, resp. it doesn't guarantee that both :primary and :secondary are present. You can do that by modifying the map to this:

.map do |k, h|
  h[:country] = k
  h[:primary] ||= ""
  h[:secondary] ||= ""
  h
end
like image 137
Jakub Hampl Avatar answered Oct 05 '22 23:10

Jakub Hampl


Here's a fun, albeit confusing, way to do it.

format = Hash.new{ |h,k| h[k] = {:country => k, :primary => '', :secondary => ''} }

myarray.inject(format){ |result, (c,k,v)| result[c][k.to_sym] = v; result }.values

# => [
  {:country=>"usa", :primary=>"john", :secondary=>"steve"},
  {:country=>"france", :primary=>"lira", :secondary=>"ben"},
  {:country=>"germany", :primary=>"jeff", :secondary=>""} ]

Basically the format hash defines the way you want to generate your output. The inject call collects the results by country. The values call just grabs the actual results without the country hash keys.

like image 22
Pan Thomakos Avatar answered Oct 06 '22 00:10

Pan Thomakos