How do I reorganise a hash similar to a table transpose in Ruby? - ruby

Lets say I have the following hash that represents a multi-dimensional question and answer matrix:
{'Are you male?' => {'Yes' => {'Do you have children?' => {'No' => '75%', 'Yes' => '25%'}}}}
How would I programmatically reorganise the hash so that it looks like this:
{'Do you have children?' => {'No' => {'Are you male?' => {'Yes' => '75%'}}, 'Yes' => {'Are you male?' => {'Yes' => '25%'}}}}

Use the original excel sheet, if possible. If not, I'd do it via intermediate form. Flatten your hash to a CSV file/string:
Are you male?, Do you have children?, percentage
Yes, Yes, 25%
Yes, No, 75%
From this you can easily reconstruct whatever alternative nesting you need.

My solution:
Store the questions in combination as a hash key representing (Row)|(Column), and then put the actual options and values in an array. Then switch questions in the hash key and transpose the array. So:
{'Are you male?|Do you have children?' => [[nil, 'Yes', 'No'],['Yes', '25%', '75%']]}
becomes:
{'Do you have children?|Are you male?' => [[nil, "Yes"], ["Yes", "25%"], ["No", "75%"]]}

Related

descending sorting on hash values

Say, I have a hash which has elements like:
###EVNT-66 => 8.40,
###EVNT-108 => 9.11,
###EVNT-345 => 88.22,
###EVNT-143 => 1.26
I wanted to sort them in descending order based on the hash values. So, I wrote this:
h.sort_by {|_key, value| value}.reverse
Surprisingly enough, I got results in this fashion:
###EVNT-108 => 9.11,
###EVNT-66 => 88.22,
###EVNT-345 => 8.40,
###EVNT-143 => 1.26
which is wrong. Line with '88' in it should be at the top. I am not sure what else could I write to achieve this.
it seems sorted alphabetically. convert to numeric first.
h.sort_by {|_key, value| -value.to_f}
the minus sign could avoid the using of reverse

New hash from array of hashes

The objective of the code below is to produce a hash with the keys being the :id field of
the hashes in original_array, and the values being all elements in original_array which have that :id.
original_array = [
{:id => '123', :name => 'test'},
{:id => '123', :name => 'another test'},
{:id => '456', :name => 'yet another test'}
]
new_hash = {}
original_array.each do |a|
new_hash[a[:id]] = original_array.select {|x| x[:id] == a[:id]}
end
My code does that, but there must be some better way to do it, ideally where the hash can be created in one step. If anyone can suggest and explain one (in the hope that I might improve my understanding of this sort of thing), then it would be appreciated.
This should do it
new_hash = original_array.group_by{|h| h[:id]}
Documentation: Enumerable#group_by.

Replace keys with values in a Hash

What is the best way to replace all keys with values in a hash?
I came up with:
Hash[hash.map {|k,v| [v,k]}]
Is there a better solution?
There's a built-in method for that:
hash.invert
It's possible to invert a hash:
{ 'a' => 1, 'b' => 2 }.invert # => {1=>"a", 2=>"b"}
But be careful of the side-effects:
{ 'a' => 1, 'b' => 2, 'c' => 2 }.invert # => {1=>"a", 2=>"c"}
A hash's keys must be unique, but values don't have to be. When you invert the hash the duplicate values will collide, overwriting each other, with the last one winning.

Ruby: Create hash with default keys + values of an array

I believe this has been asked/answered before in a slightly different context, and I've seen answers to some examples somewhat similar to this - but nothing seems to exactly fit.
I have an array of email addresses:
#emails = ["test#test.com", "test2#test2.com"]
I want to create a hash out of this array, but it must look like this:
input_data = {:id => "#{id}", :session => "#{session}",
:newPropValues => [{:key => "OWNER_EMAILS", :value => "test#test.com"} ,
{:key => "OWNER_EMAILS", :value => "test2#test2.com"}]
I think the Array of Hash inside of the hash is throwing me off. But I've played around with inject, update, merge, collect, map and have had no luck generating this type of dynamic hash that needs to be created based on how many entries in the #emails Array.
Does anyone have any suggestions on how to pull this off?
So basically your question is like this:
having this array:
emails = ["test#test.com", "test2#test2.com", ....]
You want an array of hashes like this:
output = [{:key => "OWNER_EMAILS", :value => "test#test.com"},{:key => "OWNER_EMAILS", :value => "test2#test2.com"}, ...]
One solution would be:
emails.inject([]){|result,email| result << {:key => "OWNER_EMAILS", :value => email} }
Update: of course we can do it this way:
emails.map {|email| {:key => "OWNER_EMAILS", :value => email} }

Is saving a hash in another hash common practice?

I'd like to save some hash objects to a collection (in the Java world think of it as a List). I search online to see if there is a similar data structure in Ruby and have found none. For the moment being I've been trying to save hash a[] into hash b[], but have been having issues trying to get data out of hash b[].
Are there any built-in collection data structures on Ruby? If not, is saving a hash in another hash common practice?
If it's accessing the hash in the hash that is the problem then try:
>> p = {:name => "Jonas", :pos => {:x=>100.23, :y=>40.04}}
=> {:pos=>{:y=>40.04, :x=>100.23}, :name=>"Jonas"}
>> p[:pos][:x]
=> 100.23
There shouldn't be any problem with that.
a = {:color => 'red', :thickness => 'not very'}
b = {:data => a, :reason => 'NA'}
Perhaps you could explain what problems you're encountering.
The question is not completely clear, but I think you want to have a list (array) of hashes, right?
In that case, you can just put them in one array, which is like a list in Java:
a = {:a => 1, :b => 2}
b = {:c => 3, :d => 4}
list = [a, b]
You can retrieve those hashes like list[0] and list[1]
Lists in Ruby are arrays. You can use Hash.to_a.
If you are trying to combine hash a with hash b, you can use Hash.merge
EDIT: If you are trying to insert hash a into hash b, you can do
b["Hash a"] = a;
All the answers here so far are about Hash in Hash, not Hash plus Hash, so for reasons of completeness, I'll chime in with this:
# Define two independent Hash objects
hash_a = { :a => 'apple', :b => 'bear', :c => 'camel' }
hash_b = { :c => 'car', :d => 'dolphin' }
# Combine two hashes with the Hash#merge method
hash_c = hash_a.merge(hash_b)
# The combined hash has all the keys from both sets
puts hash_c[:a] # => 'apple'
puts hash_c[:c] # => 'car', not 'camel' since B overwrites A
Note that when you merge B into A, any keys that A had that are in B are overwritten.

Resources