Accessing hash values by methods vs like in array in ruby - ruby

I have a hash like this:
h = { "key1" => { "key2" => "value"}, "key3" => "value3"}
If I try to access h.key1 it won't let me, but if I do h["key1"] it will.
But when I use the session hash, I can write the following code without getting an error:
#session = session["omniauth"]
#session.data
When can I access the keys by methods and when like an array?

You can only access hash values with h["key1"] method (without using other modifiers).
The reason why #session.data works is that #session is not an instance of Hash, but its an instance of OmniAuth::AuthHash which supports both methods to access values.
So it depends on a type of an object you are working with.

You can access hash key by dot notation with the help of OpenStruct
require 'ostruct'
h = { "key1" => { "key2" => "value"}, "key3" => "value3"}
open_struct = OpenStruct.new(h)
p open_struct.key1
I hope It may help you to solve your issue

The reason you are able to access key-value from the session object is that someone has defined the method [] on it.
If you wanted to access h.key1 on your hash, use OpenStruct:
h = OpenStruct({ "key1" => { "key2" => "value"}, "key3" => "value3"})
This would return the following results:
h.key1 # { "key2" => "value }
h.key3 # "value3"

Related

Define a hash with keys of class string without using hash rockets?

A hash (with key of class String) can be defined using hash rocket syntax like so:
h = {:name => 'Charles', "name" => 'John'}
Is there a way to define it using another notation?
I tried:
a = {name: "Charles", "name": "John"}
(irb):14: warning: key :name is duplicated and overwritten on line 14
a
# => {:name=>"John"}
Also note that without any overriding, I still can't spot a way to get the key to be class String:
b = {"name": "John"}
b
# => {:name=>"John"} # "name" (String) is stored as :name (Symbol)
AFAIK it's not possible to define a hash with any String keys using the more modern hash notation (the notation that doesn't use the hash rockets). Is there any way, or should the => has rockets be used when defining a hash with a key of class String?
The documentation really makes this pretty clear:
The older syntax for Hash data uses the “hash rocket,” =>:
h = {:foo => 0, :bar => 1, :baz => 2} h # => {:foo=>0, :bar=>1,
> :baz=>2}
Alternatively, but only for a Hash key that's a Symbol, you can use a
newer JSON-style syntax...

How to generate direct access keys to nested hash which contains hash and arrays as values?

I want to compare two XML files where one is input and the other is output. I am converting both into a hash.
My idea is to get all the keys from the input XML converted to hash, and search each key in both the input and output hashes for their respective key/value pairs.
I have a hash:
{
"requisition_header" => {
"requested_by" => {"login" => "coupasupport"},
"department" => {"name" => "Marketing"},
"ship_to_address" => {"name" => "Address_1431693296"},
"justification" => nil,
"attachments" => [],
"requisition_lines" => [
{
"description" => "Cleaning Services for Building A",
"line_num" => 1,
"need_by_date" => 2010-09-23 07:00:00 UTC,
"source_part_num" => nil,
"supp_aux_part_num" => nil,
"unit_price" => #<BigDecimal:a60520c,'0.3E4',9(18)>,
"supplier" => {"name" => "amazon.com"},
"account" => {
"code" => "SF-Marketing-Indirect",
"account_type" => {"name" => "Ace Corporate"}
},
"currency" => {"code" => "USD"},
"payment_term" => {"code" => "Net 30"},
"shipping_term" => {"code" => "Standard"},
"commodity" => {"name" => "Marketing-Services"}
}
]
}
}
It is nested and all the values are not directly accessible.
I want a way to generate direct access to each value in the hash.
For example:
requisition_header.requested_by.login
will access "coupasupport".
requisition_header.department.name
will access "Marketing".
requisition_header.requisition_lines[0].description
will access "Cleaning Services for Building A".
requisition_header.requisition_lines[0].line_num
will access "1".
requisition_header.requisition_lines[0].need_by_date
will access "2010-09-23 07:00:00 UTC".
Each key built can be used to search for the value directly inside the hash.
That could be done with the following method, that translates the nested hash into nested OpenStructs:
require 'ostruct'
def deep_structify(hash)
result = {}
hash.each do |key, value|
result[key] = value.is_a?(Hash) ? deep_structify(value) : value
end if hash
OpenStruct.new(result)
end
hash = {"requisition_header"=>{"requested_by"=>{"login"=>"coupasupport"}, "department"=>{"name"=>"Marketing"}, "ship_to_address"=>{"name"=>"Address_1431693296"}, "justification"=>nil, "attachments"=>[], "requisition_lines"=>[{"description"=>"Cleaning Services for Building A", "line_num"=>1, "need_by_date"=>2010-09-23 07:00:00 UTC, "source_part_num"=>nil, "supp_aux_part_num"=>nil, "unit_price"=>#<BigDecimal:a60520c,'0.3E4',9(18)>, "supplier"=>{"name"=>"amazon.com"}, "account"=>{"code"=>"SF-Marketing-Indirect", "account_type"=>{"name"=>"Ace Corporate"}}, "currency"=>{"code"=>"USD"}, "payment_term"=>{"code"=>"Net 30"}, "shipping_term"=>{"code"=>"Standard"}, "commodity"=>{"name"=>"Marketing-Services"}}]}}
struct = deep_structify(hash)
struct.requisition_header.department.name
#=> "Marketing"
You can do it by overriding OpenStruct#new as well,
require 'ostruct'
class DeepStruct < OpenStruct
def initialize(hash=nil)
#table = {}
#hash_table = {}
if hash
hash.each do |k,v|
#table[k.to_sym] = (v.is_a?(Hash) ? self.class.new(v) : v)
#hash_table[k.to_sym] = v
new_ostruct_member(k)
end
end
end
def to_h
#hash_table
end
end
Now you can do:
require 'deep_struct'
hash = {"requisition_header"=>{"requested_by"=>{"login"=>"coupasupport"}, "department"=>{"name"=>"Marketing"}, "ship_to_address"=>{"name"=>"Address_1431693296"}, "justification"=>nil, "attachments"=>[], "requisition_lines"=>[{"description"=>"Cleaning Services for Building A", "line_num"=>1, "need_by_date"=>2010-09-23 07:00:00 UTC, "source_part_num"=>nil, "supp_aux_part_num"=>nil, "unit_price"=>#<BigDecimal:a60520c,'0.3E4',9(18)>, "supplier"=>{"name"=>"amazon.com"}, "account"=>{"code"=>"SF-Marketing-Indirect", "account_type"=>{"name"=>"Ace Corporate"}}, "currency"=>{"code"=>"USD"}, "payment_term"=>{"code"=>"Net 30"}, "shipping_term"=>{"code"=>"Standard"}, "commodity"=>{"name"=>"Marketing-Services"}}]}}
mystruct = DeepStruct.new hash
mystruct.requisition_header.requested_by.login # => coupasupport
mystruct.requisition_header.to_h # => {"requested_by"=>{"login"=>"coupasupport"}
You could use BasicObject#method_missing:
Code
class Hash
def method_missing(key,*args)
(args.empty? && key?(key)) ? self[key] : super
end
end
Example
hash = { animals: {
pets: { dog: "Diva", cat: "Boots", python: "Stretch" },
farm: { pig: "Porky", chicken: "Little", sheep: "Baa" }
},
finishes: {
tinted: { stain: "Millers", paint: "Oxford" },
clear: { lacquer: "Target", varnish: "Topcoat" }
}
}
hash.finishes.tinted.stain
#=> "Millers
hash.animals.pets.cat
#=> "Boots"
hash.animals.pets
#=> {:dog=>"Diva", :cat=>"Boots", :python=>"Stretch"}
hash.animals
#=> {:pets=>{:dog=>"Diva", :cat=>"Boots", :python=>"Stretch"},
# :farm=>{:pig=>"Porky", :chicken=>"Little", :sheep=>"Baa"}}
Reader challenge
There is a potential "gotcha" with this approach. I leave it to the reader to identify it. My example contains a clue. (Mind you, there may be other problems I haven't thought of.)

How to access a symbol hash key using a variable in Ruby

I have an array of hashes to write a generic checker for, so I want to pass in the name of a key to be checked. The hash was defined with keys with symbols (colon prefixes). I can't figure out how to use the variable as a key properly. Even though the key exists in the hash, using the variable to access it results in nil.
In IRB I do this:
>> family = { 'husband' => "Homer", 'wife' => "Marge" }
=> {"husband"=>"Homer", "wife"=>"Marge"}
>> somevar = "husband"
=> "husband"
>> family[somevar]
=> "Homer"
>> another_family = { :husband => "Fred", :wife => "Wilma" }
=> {:husband=>"Fred", :wife=>"Wilma"}
>> another_family[somevar]
=> nil
>>
How do I access the hash key through a variable? Perhaps another way to ask is, how do I coerce the variable to a symbol?
You want to convert your string to a symbol first:
another_family[somevar.to_sym]
If you want to not have to worry about if your hash is symbol or string, simply convert it to symbolized keys
see: How do I convert a Ruby hash so that all of its keys are symbols?
You can use the Active Support gem to get access to the with_indifferent_access method:
require 'active_support/core_ext/hash/indifferent_access'
> hash = { somekey: 'somevalue' }.with_indifferent_access
=> {"somekey"=>"somevalue"}
> hash[:somekey]
=> "somevalue"
> hash['somekey']
=> "somevalue"
Since your keys are symbols, use symbols as keys.
> hash = { :husband => 'Homer', :wife => 'Marge' }
=> {:husband=>"Homer", :wife=>"Marge"}
> key_variable = :husband
=> :husband
> hash[key_variable]
=> "Homer"
If you use Rails with ActiveSupport, then do use HashWithIndifferentAccess for flexibility in accessing hash with either string or symbol.
family = HashWithIndifferentAccess.new({
'husband' => "Homer",
'wife' => "Marge"
})
somevar = "husband"
puts family[somevar]
#Homer
somevar = :husband
puts family[somevar]
#Homer
The things that you see as a variable-key in the hash are called Symbol is a structure in Ruby. They're primarily used either as hash keys or for referencing method names. They're immutable, and Only one copy of any symbol exists at a given time, so they save memory.
You can convert a string or symbol with .to_sym or a symbol to string with .to_s to illustrate this let me show this example:
strings = ["HTML", "CSS", "JavaScript", "Python", "Ruby"]
symbolArray = [:HTML, :CSS, :JavaScript, :Python, :Ruby]
# Add your code below!
symbols = Array.new
strings.each {|x|
symbols.push(x.to_sym)
}
string = Array.new
symbolArray .each {|x|
string.push(x.to_s)
}
print symbols
print string
the result would be:
[:HTML, :CSS, :JavaScript, :Python, :Ruby]
["HTML", "CSS", "JavaScript", "Python", "Ruby"]
In ruby 9.1 you would see the symbols with the colons (:) in the right instead:
movies = { peter_pan: "magic dust", need_4_speed: "hey bro", back_to_the_future: "hey Doc!" }
I just wanted to make this point a litter more didactic so who ever is reading this can used.
One last thing, this is another way to solve your problem:
movie_ratings = {
:memento => 3,
:primer => 3.5,
:the_matrix => 3,
}
# Add your code below!
movie_ratings.each_key {|k|
puts k.to_s
}
result:
memento
primer
the_matrix

Extract value nested with an array of hashes in Ruby

I have an XML response from a URL that is being converted into an array of hashes which looks like:
{
"EmployeeList"=>{
"EmployeeProfile"=>{
"BuildLoc"=>{"$"=>"1 Happy Place"},
"Status"=>{"$"=>"A"},
"SecrTitle"=>[{}, {}],
"ID"=>{},
"bct"=>{},
"NUM"=>{"$"=>"1234567"},
"BuildCity"=>{"$"=>"Dayton"},
"BuildFloor"=>{"$"=>"6"},
"Expense"=>{"$"=>"1345"},
"LastName"=>{"$"=>"Smith"},
"Middle"=>{},
"SecrName"=>[{}, {}],
"InternalSMTPAddress"=>{"$"=>"Joe.Smith#happy.com"},
"IAddress"=>{"$"=>"Joe.Smith#happy.com"},
"PreferredLastName"=>{},
"DisplayName"=>{"$"=>"Joe Smith"},
"CellPhoneNo"=>{},
"Title"=>{"$"=>"Dr."},
"BuildStreetAddress"=>{"$"=>"123 Happy town"},
"BuildState"=>{"$"=>"IL"},
"FirstName"=>{"$"=>"Joe"},
"AltContactTitle1"=>{},
"Dept-CostCtrNo"=>{"$"=>"129923"},
"PreferredFirstName"=>{"$"=>"Joe"},
"AltContactName2"=>{},
"AltContactPhone2"=>{},
"GDP"=>{},
"BuildZip"=>{"$"=>"112345"},
"RegionID"=>{"$"=>"NAMR"},
"EmploymentType"=>{"$"=>"E"},
"TempPhone"=>{},
"BuildID"=>{"$"=>"01114"},
"CountryAbbr"=>{"$"=>"USA"},
"FaxDisp1"=>{},
"BuildCountry"=>{"$"=>"United States"}
}
},
nil=>nil
}
What's the easiest way to extract the value of "DisplayName" and "InternalSMTPAddress"?
If you assign the returned hash to a variable named "hash" you can access the two desired values for those keys like:
hash['EmployeeList']['EmployeeProfile']['DisplayName']
=> {"$"=>"Joe Smith"}
and
hash['EmployeeList']['EmployeeProfile']['InternalSMTPAddress']
=> {"$"=>"Joe.Smith#happy.com"}
If you want the actual data in them add a trailing ['$']:
hash['EmployeeList']['EmployeeProfile']['DisplayName']['$']
=> "Joe Smith"
hash['EmployeeList']['EmployeeProfile']['InternalSMTPAddress']['$']
=> "Joe.Smith#happy.com"
If you need to find some key in nested hashes use this method:
def find_key(hash,key)
hash.each {|k, v|
return v if k==key
tmp=find_key(v,key) if v.is_a?(Hash)
return tmp unless tmp.nil?
}
return nil
end
Usage:
hash = Hash.new
hash["key1"] = "value1"
hash["key2"] = "value2"
hash["key3"] = Hash.new
hash["key3"]["key4"] = "value4"
hash["key3"]["key5"] = "value5"
hash["key6"] = Hash.new
hash["key6"]["key7"] = "value7"
hash["key6"]["key8"] = Hash.new
hash["key6"]["key8"]["key9"] = "value9"
find_key(hash,"key9") => "value9"
find_key(hash,"key8") => {"key9"=>"value9"}
find_key(hash,"dsfsdfsd") => nil
I would recommend you to use ruby gem nested_lookup.
Install the gem using command gem install nested_lookup
In your case you need the value of 'DisplayName' and 'InternalSMTPAddress'. This is what you need to do.
Rameshs-MacBook-Pro:~ rameshrv$ irb
irb(main):001:0> require 'nested_lookup'
=> true
irb(main):051:0> include NestedLookup
=> Object
# Here the test_data is the data you gave in the question
irb(main):052:0> test_data.nested_get('DisplayName')
=> {"$"=>"Joe Smith"}
irb(main):053:0> test_data.nested_get('InternalSMTPAddress')
=> {"$"=>"Joe.Smith#happy.com"}
irb(main):054:0>
For more information please read https://github.com/rameshrvr/nested_lookup

Ruby - getting value of hash

I have a hash like
{:key1 => "value1", :key2 => "value2"}
And I have a variable k which will have the value as 'key1' or 'key2'.
I want to get the value of k into a variable v.
Is there any way to achieve this with out using if or case? A single line solution is preferred. Please help.
Convert the key from a string to a symbol, and do a lookup in the hash.
hash = {:key1 => "value1", :key2 => "value2"}
k = 'key1'
hash[k.to_sym] # or iow, hash[:key1], which will return "value1"
Rails uses this class called HashWithIndifferentAccess that proves to be very useful in such cases. I know that you've only tagged your question with Ruby, but you could steal the implementation of this class from Rails' source to avoid string to symbol and symbol to string conversions throughout your codebase. It makes the value accessible by using a symbol or a string as a key.
hash = HashWithIndifferentAccess.new({:key1 => "value1", :key2 => "value2"})
hash[:key1] # "value1"
hash['key1'] # "value1"

Resources