Parsing XML HTTParty response - ruby-on-rails

I was getting data from an api using HTTParty. I managed to get back the XML response, and it looks like HTTParty has made that xml response into Ruby.
How can I navigate through the hashes and get the data I need? For instance, say I wanted the "Name", which is "29TH AVENUE STN/ARBUTUS".
The API key works fine, since I am getting a response back.
Just not too sure how I can navigate through and get the data I want, and to put it into my view.
index.html.erb:
<% #courses.each do |course| %>
<%= course %>
<% end %>
HTTparty response:
["Route", {
"RouteNo" => "016", "Name" => "29TH AVENUE STN/ARBUTUS ", "OperatingCompany" => "CMBC", "Patterns" => {
"Pattern" => [{
"PatternNo" => "E5TP", "Destination" => "29TH AVE STN", "RouteMap" => {
"Href" => "http://nb.translink.ca/geodata/trip/016-E5TP.kmz"
}, "Direction" => "EAST"
}, {
"PatternNo" => "EB1", "Destination" => "29TH AVE STN", "RouteMap" => {
"Href" => "http://nb.translink.ca/geodata/trip/016-EB1.kmz"
}, "Direction" => "EAST"
}, {
"PatternNo" => "EB5", "Destination" => "29TH AVE STN", "RouteMap" => {
"Href" => "http://nb.translink.ca/geodata/trip/016-EB5.kmz"
}, "Direction" => "EAST"
}]
}
}]
Model for getting the response
class Checker
include HTTParty
base_uri 'http://api.translink.ca/rttiapi/v1/routes'
default_params apikey: "[my proper api key]"
format :xml
def self.for term
get("", query: {stopNo: term})["Routes"]["Route"]["RouteNo"]
end
end
Controller for the Model
class TimesController < ApplicationController
def index
#search_term = '51048'
#courses = Checker.for(#search_term)
end
end

Assuming you have a response shown:
resp = ["Route", {
"RouteNo" => "016", "Name" => "29TH AVENUE STN/ARBUTUS ", "OperatingCompany" => "CMBC", "Patterns" => {
"Pattern" => [{
"PatternNo" => "E5TP", "Destination" => "29TH AVE STN", "RouteMap" => {
"Href" => "http://nb.translink.ca/geodata/trip/016-E5TP.kmz"
}, "Direction" => "EAST"
}, {
"PatternNo" => "EB1", "Destination" => "29TH AVE STN", "RouteMap" => {
"Href" => "http://nb.translink.ca/geodata/trip/016-EB1.kmz"
}, "Direction" => "EAST"
}, {
"PatternNo" => "EB5", "Destination" => "29TH AVE STN", "RouteMap" => {
"Href" => "http://nb.translink.ca/geodata/trip/016-EB5.kmz"
}, "Direction" => "EAST"
}]
}
}]
which is in turn an array of two elements, first being 'Route' and the last being a hash with all properties, to get the name use simply:
resp.last['Name']

Related

monogid pluck returns duplicate results for embedded/nested fields

Monogoid pluck returns duplicate embedded results (not concerned about duplicate rows) for embedded fields.
eg: (user is embedded document for SomeModel)
SomeModel.where(condition).pluck(:region, "user.name", "user.lastname")
Results:
[["amr",
{"name" => "mark", "lastname" => "goodman"},
{"name" => "mark", "lastname" => "goodman"}],
["amr",
{"name" => "john", "lastname" => "cena"},
{"name" => "john", "lastname" => "cena"}]
]
I was expecting something like below:
[["amr",
{"name" => "mark"},
{"lastname" => "goodman"}],
["amr",
{"name" => "john"},
{"lastname" => "cena"}]
]
Similarly, if I query multiple fields from embedded doc, it creates that many duplicate hashes.
Not sure if I am doing something wrong here.
I'm not sure why that's the case, but you can get the desired result using map instead of pluck:
SomeModel.where(condition).map { |m| [m.region, m.user.name, m.user.lastname] }
This should give you the results:
[
["amr", "mark", "goodman"],
["amr", "john", "cena"]
]
Or:
SomeModel.where(condition).map do |m|
[m.region, { 'name' => m.user.name }, { 'lastname' => m.user.lastname }]
end
Should give you the results:
[
["amr", { "name" => "mark" }, { "lastname" => "goodman" }],
["amr", { "name" => "john" }, { "lastname" => "cena" }]
]

Grabbing a specific hash in an array that meets a specific criteria

I have a huge array full of a bunch of hashes. What I need to do is single out one index hash from the array that meets a specific criteria. (doing this due to an rspec test, but having trouble singling out one of them)
My array is like this
[
{
"name" => "jon doe",
"team" => "team2",
"price" => 2000,
"eligibility_settings" => {}
},
{
"name" => "jonny doe",
"team" => "team1",
"value" => 2000,
"eligibility_settings" => {
"player_gender" => male,
"player_max_age" => 26,
"player_min_age" => 23,
"established_union_only" => true
}
},
{
"name" => "jonni doe",
"team" => "team3",
"price" => 2000,
"eligibility_settings" => {}
},
]
I need to single out the second one, based on its eligibility settings. I just took three of them from my array, have lots more, so simple active record methods like (hash.second) won't work in this instance.
I've tried things like
players.team.map(&:hash).find{ |x| x[ 'eligibility_settings?' ] == true}
However when I try this, I get a nil response. (which is odd)
I've also looked into using the ruby detect method, which hasn't gotten me anywhere either
Players.team.map(&:hash).['hash.seligibiltiy_settings'].detect { true }
Would anybody have any idea what to do with this one?
Notes
players.team.map(&:hash).find{ |x| x[ 'eligibility_settings?' ] == true}
Players.team.map(&:hash).['hash.seligibiltiy_settings'].detect { true }
Is is players or Players ?
Why is it plural?
If you can call map on team, it probably should be plural
Why do you convert to a hash?
eligibility_settings? isn't a key in your hash. eligibility_settings is
eligibility_settings can be a hash, but it cannot be true
If you want to check if it isn't empty, use !h['eligibility_settings'].empty?
Possible solution
You could use :
data = [
{
'name' => 'jon doe',
'team' => 'team2',
'price' => 2000,
'eligibility_settings' => {}
},
{
'name' => 'jonny doe',
'team' => 'team1',
'value' => 2000,
'eligibility_settings' => {
'player_gender' => 'male',
'player_max_age' => 26,
'player_min_age' => 23,
'established_union_only' => true
}
},
{
'name' => 'jonni doe',
'team' => 'team3',
'price' => 2000,
'eligibility_settings' => {}
}
]
p data.find { |h| !h['eligibility_settings'].empty? }
# {"name"=>"jonny doe", "team"=>"team1", "value"=>2000, "eligibility_settings"=>{"player_gender"=>"male", "player_max_age"=>26, "player_min_age"=>23, "established_union_only"=>true}}
If h['eligibility_settings'] can be nil, you can use :
data.find { |h| !h['eligibility_settings'].blank? }
or
data.find { |h| h['eligibility_settings'].present? }

Loop to create JSON object

just trying to loop thru my Story model.. and create JSON object and insert it into Call and send it.. But i'm not sure how to loop thru Stories..
I've done this:
#stories = Array.new
Story.where(newsletter_id: current_user.selected_newsletter) do |story|
#stories << {
:title => story.title,
:image_url => story.image_url
}
end
and i'm trying ti insert the loop to this JSON OBJECT
"message" => {
"attachment" => {
"type" => "template",
"payload" => {
"template_type" => "generic",
"elements" => [{this is the array}]
}
}
}
The array with multiple stories should looks like this:
[
{
"title" => "title....1",
"image" => "image....1"
},
{
"title" => "title....2",
"image" => "image....3"
}
....
]
Try the following:
#stories = Story.where(newsletter_id: current_user.selected_newsletter)
.select(:title, 'image_url as image').as_json(except: :id)
And then:
{
"message" => {
"attachment" => {
"type" => "template",
"payload" => {
"template_type" => "generic",
"elements" => #stories
}
}
}
}

What is an elegant way of modifying hashes inside an array within a nested hash in Ruby

I would like to transform this
def some_process(k,v)
return "#{v}_#{k}"
end
a_hash = {
"i_love_hashes" => {
"thing" => 20,
"other_thing" => "0",
"yet_another_thing" => "i disagree",
"_peculiar_thing" => [
{"count" => 30,
"name" => "freddie"},
{"count" => 15,
"name" => "johhno"},
{"count" => 12,
"name" => "mohammed"},
]
},
"as_do_i" => {
"thing" => 10,
"other_thing" => "2",
"yet_another_thing" => "i strongly agree",
"_peculiar_thing" => [
{"count" => 10,
"name" => "frodo"},
{"count" => 4,
"name" => "bilbo"},
{"count" => 2,
"name" => "elizabeth"},
]
}
}
into this
{
"i_love_hashes"=>{
"thing"=>20,
"other_thing"=>"0",
"yet_another_thing"=>"i disagree",
"_peculiar_thing"=> [
{"count"=>30, "name"=>"freddie", :sinister_name=>"freddie_i_love_hashes"},
{"count"=>15, "name"=>"johhno", :sinister_name=>"johhno_i_love_hashes"},
{"count"=>12, "name"=>"mohammed", :sinister_name=>"mohammed_i_love_hashes"}
]},
"as_do_i"=>{
"thing"=>10,
"other_thing"=>"2",
"yet_another_thing"=>"i strongly agree",
"_peculiar_thing"=>[
{"count"=>10, "name"=>"frodo", :sinister_name=>"frodo_as_do_i"},
{"count"=>4, "name"=>"bilbo", :sinister_name=>"bilbo_as_do_i"},
{"count"=>2, "name"=>"elizabeth", :sinister_name=>"elizabeth_as_do_i"}
]
}
}
this is the code I am currently using to achieve this
a_hash.each_with_object({}) do |(k,v),o|
o.merge!({k =>
v.each_with_object({}) do |(a,b),g|
g.merge!({ a =>
(b.is_a?(Array) ? b.collect {|x| x.merge({sinister_name: (some_process k, x["name"])})} : b)
})
end
})
end
Ignoring the specific details of what is being returned by "some_process" (what is important is that it depends on the outer most key and the inner name values, in this example), are there any alternatives that would be considered more elegant?
Why not do a recursive function?
def add_siniter(hash)
hash[:siniter_name] = "#{hash['name']}_i_love_hashes"
hash
end
def format_hash(item)
case item
when Hash then item.keys.each{|key| format_hash(item[key])}
when Array then item.map!{|h| add_siniter(h)}
end
end
format_hash(a_hash)

How would I filter out sub documents?

1) How can I grab all songs with more than 50 likes? The result set should return an array of songs instead of the whole document.
2) How can I return the results of a specific song id instead of the whole document?
So far I've come across this concept of virtual collections for embedded objects. However, it hasn't been implemented since. Although, on the same link, someone suggests using the aggregation framework for such a thing. Is this a viable solution?
I have the following document structure:
document = {
'date' => Time.now.utc.strftime('%Y%m%d'),
'songs' => {
'1' => {
'views' => {
'non_uniques' => 2000,
'uniques' => 203
},
'countries' => {
'us' => 123,
'uk' => 11
},
'likes' => 123,
'comments' => 123
},
'2' => {
'views' => {
'non_uniques' => 2000,
'uniques' => 203
},
'countries' => {
'us' => 123,
'uk' => 11
},
'likes' => 123,
'comments' => 123
}
},
'sign_ins' => {
'non_uniques' => 2000,
'uniques' => 203
}
}
Expected output (as requested)
For question 1), somewhere within the lines of:
['1' => {
'views' => {
'non_uniques' => 2000,
'uniques' => 203
},
'countries' => {
'us' => 123,
'uk' => 11
},
'likes' => 123,
'comments' => 123
},
'2' => {
'views' => {
'non_uniques' => 2000,
'uniques' => 203
},
'countries' => {
'us' => 123,
'uk' => 11
},
'likes' => 123,
'comments' => 123
}]
For 2):
'1' => {
'views' => {
'non_uniques' => 2000,
'uniques' => 203
},
'countries' => {
'us' => 123,
'uk' => 11
},
'likes' => 123,
'comments' => 123
}
Use aggregation framework like this:
db.songs.aggregate({$unwind:"$songs"},{$match:{"songs.likes":{$gt:50}}})
Now you can add other stages like {$group} if you want things back in one doc, or $project to change the field names, etc.
Change the '=>' delimiter in the document. Then you will have a JSON file
document = JSON.parse(document)
You will get a hash. Then check if likes > 50 in the hash
document[].each do |song|
song[].each do |id|
if id[likes].to_i > 50
#Do what you want to do
end
end
end
Hope that helps
Try:
songs_arr = []
document["songs"].keys.each do |key|
songs_arr << "insert songs here" if document["songs"][key]["likes"] > 50
end

Resources