Is saving a hash in another hash common practice? - ruby

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.

Related

Understanding map in Ruby

I'm pretty new to Ruby and am trying to understand an example of the map method that I came across:
{:a => "foo", :b => "bar"}.map{|a, b| "#{a}=#{b}"}.join('&')
which returns:
=> "a=foo&b=bar"
I don't understand how the
b=bar
is returned. The string interpolation is what is confusing me as it seems it would return something like:
=> "a=foo&bbar"
> {:a => "foo", :b => "bar"}.map{|key, value| "#{key}=#{value}"}
#=> ["a=foo", "b=bar"]
map method will fetch each element of hash as key and value pair
"#{key}=#{value}" is a String Interpolation which adds = between your key and value
Using this syntax everything between the opening #{ and closing } bits
is evaluated as Ruby code, and the result of this evaluation will be
embedded into the string surrounding it.
Array#join will returns a string created by converting each element of the array to a string, separated by the given separator.
so here in your case:
> ["a=foo", "b=bar"].join('&')
#=> "a=foo&b=bar"
In Rails you can convert hash to query params using Hash#to_query method, which will return the same result.
> {:a => "foo", :b => "bar"}.to_query
#=> "a=foo&b=bar"
The symbol key :a and the local variable a have nothing in common. The names are only coincidentally the same. Consider this code instead:
{
var1: "value1",
var2: "value2"
}.map do |key, value|
"#{key}=#{value}"
end.join('&')
# => "var1=value1&var2=value2"
Here the variables are different. What map does, like each, is iterate over each key-value pair in the Hash. That means you can do things like this, too, to simplify:
{
var1: "value1",
var2: "value2"
}.map do |pair|
pair.join('=')
end.join('&')
# => "var1=value1&var2=value2"
Normally when iterating over a Hash you should use names like k,v or key,value to be clear on what you're operating on.
If you're ever confused what's going on internally in an iteration loop, you can debug like this:
{
var1: "value1",
var2: "value2"
}.map do |pair|
puts pair.inspect
pair.join('=')
end.join('&')
That gives you this output:
[:var1, "value1"]
[:var2, "value2"]
That technique helps a lot. There's even the short-hand notation for this:
p pair
There are 2 method calls occurring here, the map and the join. One way to make it clearer and easier to understand is to separate the two methods and alter the keywords used in the map method. So instead of
{:a => "foo", :b => "bar"}.map{|a, b| "#{a}=#{b}"}.join('&')
Lets have
{:a => "foo", :b => "bar"}.map{|key, value| "#{key}=#{value}"}
This returns an array. #=> ["a=foo", "b=bar"]
Now:
["a=foo", "b=bar"].join('&')
produces a sting
#=> "a=foo&b=bar"
Map is iterating over the two key/value pairs and creating a string with the '=' between them and returns it in an array. It would iterate over all the key/value pairs in the harsh. Our example just has 2.
Join attaches the two elements of the array together with the '&' symbol between them and returns it as string. It would attach all elements of the array no matter its size.
What helped me to learn map and join is to open up the irb or pry and create a few hashes and arrays and play around with them. I highly recommend using unique names for your values that explain what is going on.
I hope this helps you.

Getting an array of hash values given specific keys

Given certain keys, I want to get an array of values from a hash (in the order I gave the keys). I had done this:
class Hash
def values_for_keys(*keys_requested)
result = []
keys_requested.each do |key|
result << self[key]
end
return result
end
end
I modified the Hash class because I do plan to use it almost everywhere in my code.
But I don't really like the idea of modifying a core class. Is there a builtin solution instead? (couldn't find any, so I had to write this).
You should be able to use values_at:
values_at(key, ...) → array
Return an array containing the values associated with the given keys. Also see Hash.select.
h = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" }
h.values_at("cow", "cat") #=> ["bovine", "feline"]
The documentation doesn't specifically say anything about the order of the returned array but:
The example implies that the array will match the key order.
The standard implementation does things in the right order.
There's no other sensible way for the method to behave.
For example:
>> h = { :a => 'a', :b => 'b', :c => 'c' }
=> {:a=>"a", :b=>"b", :c=>"c"}
>> h.values_at(:c, :a)
=> ["c", "a"]
i will suggest you do this:
your_hash.select{|key,value| given_keys.include?(key)}.values

How to combine one hash with another hash in ruby

I have two hashes...
a = {:a => 5}
b = {:b => 10}
I want...
c = {:a => 5,:b => 10}
How do I create hash c?
It's a pretty straight-forward operation if you're just interleaving:
c = a.merge(b)
If you want to actually add the values together, this would be a bit trickier, but not impossible:
c = a.dup
b.each do |k, v|
c[k] ||= 0
c[k] += v
end
The reason for a.dup is to avoid mangling the values in the a hash, but if you don't care you could skip that part. The ||= is used to ensure it starts with a default of 0 as nil + 1 is not valid.
(TL;DR: hash1.merge(hash2))
As everyone is saying you can use merge method to solve your problem. However there is slightly some problem with using the merge method. Here is why.
person1 = {"name" => "MarkZuckerberg", "company_name" => "Facebook", "job" => "CEO"}
person2 = {"name" => "BillGates", "company_name" => "Microsoft", "position" => "Chairman"}
Take a look at these two fields name and company_name. Here name and company_name both are same in the two hashes(I mean the keys). Next job and position have different keys.
When you try to merge two hashes person1 and person2 If person1 and person2 keys are same? then the person2 key value will override the peron1 key value . Here the second hash will override the first hash fields because both are same. Here name and company name are same. See the result.
people = person1.merge(person2)
Output: {"name"=>"BillGates", "company_name"=>"Microsoft",
"job"=>"CEO", "position"=>"Chairman"}
However if you don't want your second hash to override the first hash. You can do something like this
people = person1.merge(person2) {|key, old, new| old}
Output: {"name"=>"MarkZuckerberg", "company_name"=>"Facebook",
"job"=>"CEO", "position"=>"Chairman"}
It is just a quick note when using merge()
I think you want
c = a.merge(b)
you can check out the docs at http://www.ruby-doc.org/core-1.9.3/Hash.html#method-i-merge
Use merge method:
c = a.merge b

How to add new item to hash

I don't know how to add new item to already existing hash. For example, first I construct hash:
hash = {item1: 1}
After that, I want to add item2, so after this I have hash like this:
{item1: 1, item2: 2}
I don't know what method to do on hash. Could someone help me?
Create the hash:
hash = {:item1 => 1}
Add a new item to it:
hash[:item2] = 2
If you want to add new items from another hash - use merge method:
hash = {:item1 => 1}
another_hash = {:item2 => 2, :item3 => 3}
hash.merge(another_hash) # {:item1=>1, :item2=>2, :item3=>3}
In your specific case it could be:
hash = {:item1 => 1}
hash.merge({:item2 => 2}) # {:item1=>1, :item2=>2}
but it's not wise to use it when you should to add just one element more.
Pay attention that merge will replace the values with the existing keys:
hash = {:item1 => 1}
hash.merge({:item1 => 2}) # {:item1=>2}
exactly like hash[:item1] = 2
Also you should pay attention that merge method (of course) doesn't effect the original value of hash variable - it returns a new merged hash. If you want to replace the value of the hash variable then use merge! instead:
hash = {:item1 => 1}
hash.merge!({:item2 => 2})
# now hash == {:item1=>1, :item2=>2}
hash.store(key, value) - Stores a key-value pair in hash.
Example:
hash #=> {"a"=>9, "b"=>200, "c"=>4}
hash.store("d", 42) #=> 42
hash #=> {"a"=>9, "b"=>200, "c"=>4, "d"=>42}
Documentation
It's as simple as:
irb(main):001:0> hash = {:item1 => 1}
=> {:item1=>1}
irb(main):002:0> hash[:item2] = 2
=> 2
irb(main):003:0> hash
=> {:item1=>1, :item2=>2}
hash[key]=value
Associates the value given by value with the key given by key.
hash[:newKey] = "newValue"
From Ruby documentation:
http://www.tutorialspoint.com/ruby/ruby_hashes.htm
hash_items = {:item => 1}
puts hash_items
#hash_items will give you {:item => 1}
hash_items.merge!({:item => 2})
puts hash_items
#hash_items will give you {:item => 1, :item => 2}
hash_items.merge({:item => 2})
puts hash_items
#hash_items will give you {:item => 1, :item => 2}, but the original variable will be the same old one.
Create hash as:
h = Hash.new
=> {}
Now insert into hash as:
h = Hash["one" => 1]

merging arrays of hashes

I have two arrays, each holding arrays with attribute hashes.
Array1 => [[{attribute_1 = A}, {attribute_2 = B}], [{attribute_1 = A}, {attribute_4 = B}]]
Array2 => [{attribute_3 = C}, {attribute_2 = D}], [{attribute_3 = C, attribute_4 = D}]]
Each array in the array is holding attribute hashes for an object. In the above example, there are two objects that I'm working with. There are two attributes in each array for each of the two objects.
How do I merge the two arrays? I am trying to get a single array of 'object' arrays (there is no way to get a single array from the start because I have to make two different API calls to get these attributes).
DesiredArray => [[{attribute_1 = A, attribute_2 = B, attribute_3 = C, attribute_4 = D}],
[{attribute_1 = A, attribute_2 = B, attribute_3 = C, attribute_4 = D}]]
I've tried a couple things, including the iteration methods and the merge method, but I've been unable to get the array I need.
You seem to have parallel arrays of hashes. We can use zip to turn the parallel arrays into a single array of arrays of hashes. We can then map each array of hashes into a single hash using inject and merge:
#!/usr/bin/ruby1.8
require 'pp'
array1 = [{:attribute_1 => :A, :attribute_2 => :B}, {:attribute_1 => :A, :attribute_4 => :B}]
array2 = [{:attribute_3 => :C, :attribute_2 => :D}, {:attribute_3 => :C, :attribute_4 => :D}]
pp array1.zip(array2).collect { |array| array.inject(&:merge) }
# => [{:attribute_2=>:D, :attribute_1=>:A, :attribute_3=>:C},
# => {:attribute_4=>:D, :attribute_1=>:A, :attribute_3=>:C}]
I don't think my answer is valid anymore, since the question has been edited later.
Here, first I am fixing your array and hash notation in your question.
Array1 = [{'attribute_1' => 'A', 'attribute_2' => 'B'}, {'attribute_1' => 'A', 'attribute_2' => 'B'}]
#=> [{"attribute_1"=>"A", "attribute_2"=>"B"}, {"attribute_1"=>"A", "attribute_2"=>"B"}]
Array2 = [{'attribute_3' => 'C', 'attribute_2' => 'D'}, {'attribute_3' => 'C', 'attribute_4' => 'D'}]
#=> [{"attribute_2"=>"D", "attribute_3"=>"C"}, {"attribute_3"=>"C", "attribute_4"=>"D"}]
You can simply concatenate the two arrays to get your desired array like so:
DesiredArray = Array1+Array2
# => [{"attribute_1"=>"A", "attribute_2"=>"B"}, {"attribute_1"=>"A", "attribute_2"=>"B", {"attribute_2"=>"D", "attribute_3"=>"C"}, {"attribute_3"=>"C", "attribute_4"=>"D"}]

Resources