Easy way to access hashes from within arrays with ruby - ruby

I'm using ruby to get get some address information from the Google geocode API. I get JSON returned and parse this into a variable which is an array containing hashes.
After I've parsed the json it looks like this
{"status"=>"OK",
"results"=>
[{"address_components"=>
[{"long_name"=>"XXX", "types"=>["street_number"], "short_name"=>"XXX"},
{"long_name"=>"St Georges Terrace",
"types"=>["route"],
"short_name"=>"St Georges Terrace"},
{"long_name"=>"Perth",
"types"=>["locality", "political"],
"short_name"=>"Perth"},
{"long_name"=>"Western Australia",
"types"=>["administrative_area_level_1", "political"],
"short_name"=>"WA"},
{"long_name"=>"Australia",
"types"=>["country", "political"],
"short_name"=>"AU"},
{"long_name"=>"6000", "types"=>["postal_code"], "short_name"=>"6000"}],
"types"=>["street_address"],
"geometry"=>
{"location_type"=>"ROOFTOP",
"viewport"=>
{"northeast"=>{"lng"=>115.86768092068, "lat"=>-31.9540383793198},
"southwest"=>{"lng"=>115.86138567932, "lat"=>-31.9603336206802}},
"location"=>{"lng"=>115.8645333, "lat"=>-31.957186}},
"formatted_address"=>"XXX St Georges Terrace, Perth WA 6000, Australia"}]}
My ruby looks like this
require 'rubygems'
require 'json'
require 'open-uri'
require 'pp'
#url = "http://maps.googleapis.com/maps/api/geocode/json?address=PERTH+XXX+St+Georges+Terrace,+Western+Australia&sensor=false"
uri = URI.parse(#url)
json = uri.open.read
parsed_json = JSON.parse(json)
pp parsed_json
The mixture of hashjes and arrays is confusing me in Ruby. I wish to extract the information into one hash to look something like this
result = { "address_line_one" => "XXX St Georges Terrace", "address_line_two" => "Perth", "state" => "Western Austalia", "postal_code" => "6000" }
Thanks

j = parsed_json
a = j['results'][0]['address_components'].map { |e| e['long_name'] }
p(
{ 'address_line_one' => a[0..1].join(' '),
'address_line_two' => a[2],
'state' => a[3],
'postal_code' => a[5] }
)

Related

How do I add a parameter to a URL?

I'm trying to open multiple HTML documents. The URL for each site looks like this:
http://www.website.com/info/state=AL
AL is Alabama, but it changes by the state. I can create an array with all the two letter combinations state=('aa'..'zz').to_a, but how can I input this into the parameter were AL is above?
I want it to pull up the HTML document for all two letter combinations, and from there I can use a conditional to weed out the ones I don't want. But how should I go about inserting the two letter combinations?
Ruby's URI class is useful. It's not the most full-featured package for handling URLs out there -- check out Addressable::URI if you need more, but it's good:
require 'uri'
uri = URI.parse('http://www.website.com/info')
{
'Alabama' => 'AL',
'Alaska' => 'AK',
'Arizona' => 'AZ',
'Arkansas' => 'AR',
'California' => 'CA',
}.each_pair do |k, v|
uri.query = URI.encode_www_form( {'state' => v} )
puts uri.to_s
end
Which outputs:
http://www.website.com/info?state=AL
http://www.website.com/info?state=AK
http://www.website.com/info?state=AZ
http://www.website.com/info?state=AR
http://www.website.com/info?state=CA
Or:
%w[AL AK AZ AR CA].each do |s|
uri.query = URI.encode_www_form( {'state' => s} )
puts uri.to_s
end
Which outputs the same thing.

How do I extract the hash from an array of one hash?

I'm writing an API parser at the moment, and I'm working on formatting the data nicely.
So far, I have the following code:
data.each {|season| episodes[season["no"].to_i] = season["episode"].group_by{|i| i["seasonnum"].to_i}}
However, the only issue with this is that the output comes out like this:
8 => {
1 => [
[0] {
"epnum" => "150",
"seasonnum" => "01",
"prodnum" => "3X7802",
"airdate" => "2012-10-03",
"link" => "http://www.tvrage.com/Supernatural/episodes/1065195189",
"title" => "We Need to Talk About Kevin"
}
],
2 => [
[0] {
"epnum" => "151",
"seasonnum" => "02",
"prodnum" => "3X7803",
"airdate" => "2012-10-10",
"link" => "http://www.tvrage.com/Supernatural/episodes/1065217045",
"title" => "What's Up, Tiger Mommy?"
}
]
}
So there's a redundant array in each value of the secondary hash. How would I remove this array and just have the inside hash? So, for example I want:
8 => {
1 => {
"epnum" => "150",
"seasonnum" => "01",
"prodnum" => "3X7802",
"airdate" => "2012-10-03",
"link" => "http://www.tvrage.com/Supernatural/episodes/1065195189",
"title" => "We Need to Talk About Kevin"
}
,
etc.
EDIT: Here's the full file:
require 'httparty'
require 'awesome_print'
require 'debugger'
require 'active_support'
episodes = Hash.new{ [] }
response = HTTParty.get('http://services.tvrage.com/feeds/episode_list.php?sid=5410')
data = response.parsed_response['Show']['Episodelist']["Season"]
data.each { |season|
episodes[season["no"].to_i] = season["episode"].group_by{ |i|
i["seasonnum"].to_i
}
}
ap episodes
Input data: http://services.tvrage.com/feeds/episode_list.php?sid=5410
Wild guess:
data.each { |season|
episodes[season["no"].to_i] = season["episode"].group_by{ |i|
i["seasonnum"].to_i
}.first
}
It looks like you're using group_by (array of entries with same key) when you really want index_by (one entry per key).
data.each {|season| episodes[season["no"].to_i] = season["episode"].index_by {|i| i["seasonnum"].to_i}}
NOTE: If you can have MORE than one episode with the same seasonnum, you SHOULD use group by and have an array of values here. If you're just building a hash of episodes with a convenient lookup (one to one mapping), then index_by is what you want.

Databasedotcom materialize a custom object in Ruby

I would like to return the results of a SOQL query as JSON, but the data seems to be returned as a string.
client = SFDC_Adapter.login
data = client.query("SELECT MarkupAmount__c,
MarkupPercent__c,
Product_Type_Id__c,
Product_Type__c
FROM Product_Type__c
WHERE Product_Type_Id__c = #{product_type_id}")
p data
=> [#<Product_Type__c:0x00000001c356f8 #Id=nil, #OwnerId=nil, #IsDeleted=nil, #Name=nil, #CreatedDate=nil, #CreatedById=nil, #LastModifiedDate=nil, #LastModifiedById=nil, #SystemModstamp=nil, #MarkupPercent__c=5.0, #Subscription__c=nil, #Product_Type__c="Research Trip", #MarkupAmount__c=nil, #Product_Type_Id__c=36.0>]
puts data
=> #<Product_Type__c:0x00000001c356f8>
puts data.to_json
=> ["#<Product_Type__c:0x00000001c356f8>"]
How do I materialize these results into a JSON object for use in a Restful service?
I don't know that gem, but from looking at your output, and glancing at your results, it looks like you got a Product_Type object back.
When you use p or puts, inspect is being used, which is turning the instance into something viewable in a web-page, by using an HTML encoding on it. That's why you see < and > in the output.
Instead, you need to access the values in the object. According to the docs, you can use standard getters or using a hash[key] form to do that:
contact = Contact.find("contact_id") #=> #
contact = Contact.find_by_Name("John Smith") #=> dynamic finders!
contacts = Contact.all #=> a Databasedotcom::Collection of Contact instances
contacts = Contact.find_all_by_Company("IBM") #=> a Databasedotcom::Collection of matching Contacts
contact.Name #=> the contact's Name attribute
contact["Name"] #=> same thing
contact.Name = "new name" #=> change the contact's Name attribute, in memory
contact["Name"] = "new name" #=> same thing
contact.save #=> save the changes to the database
contact.update_attributes "Name" => "newer name",
"Phone" => "4156543210" #=> change several attributes at once and save them
contact.delete #=> delete the contact from the database
Try data['Product_Type_Id'] and you should get 36.0. An alternate way of doing the same thing is data.Product_Type_Id.
Once you have your accessors figured out you can generate JSON using a simple hash or array of hashes. This would generate a hash:
require 'json'
hash = {
'Id' => data.Id,
'OwnerId' => data.OwnerId,
'IsDeleted' => data.IsDeleted,
'Name' => data.Name,
'CreatedDate' => data.CreatedDate,
'CreatedById' => data.CreatedById,
'LastModifiedDate' => data.LastModifiedDate,
'LastModifiedById' => data.LastModifiedById,
'SystemModstamp' => data.SystemModstamp,
'MarkupPercent' => data.MarkupPercent,
'Subscription' => data.Subscription,
'Product_Type' => data.Product_Type,
'MarkupAmount' => data.MarkupAmount,
'Product_Type_Id' => data.Product_Type_Id,
}
puts hash.to_json
I didn't see a to_h or to_hash method which would be a shortcut.

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: Reading Arrays of Hashes from YAML

I have two dads going into my YAML file, but only one family comes out. What happened to Sam? How do I get both out?
## dads.rb
require 'yaml'
require 'pp'
dad=[]
dad[0] = {:name => "Joe", :kids => ["Mary", "John"]}
dad[1] = {:name => "Sam", :kids => ["Sam Jr", "Samantha", "Samizdat"]}
open('dads.yml' , 'w') do |f|
dad.each do |d|
f.write YAML::dump(d)
end
end
family = []
open('dads.yml') do |f|
family << YAML::load(f.read)
end
pp fams
You dump multiple YAML documents but only read back one. Instead, you can just dump and read the whole array:
require 'yaml'
dads = []
dads << {:name => "Joe", :kids => ["Mary", "John"]}
dads << {:name => "Sam", :kids => ["Sam Jr", "Samantha", "Samizdat"]}
open('dads.yml', 'w') { |f| YAML::dump(dads, f) }
family = YAML::load(File.read('dads.yml'))
p family
Your code currently creates separate "documents" within the YAML output. By default, YAML::load will just read in the first document. Niklas' answer is definitely the way you should go, but if you absolutely had to deal with multiple documents, you could use the load_documents method:
family = YAML.load_documents(File.read("dads.yml"))
# => [{:name=>"Joe", :kids=>["Mary", "John"]}, {:name=>"Sam", :kids=>["Sam Jr", "Samantha", "Samizdat"]}]

Resources