how to read individual data from the content of remote JSON file in Ruby on Rails? - ruby-on-rails

I am trying to read individual data from the content of json API on Oil and Gas Authority website; however the code I have returns all the data. Any help would be highly appreciated.
require 'json'
require 'open-uri'
def index
url='http://data-ogauthority.opendata.arcgis.com/datasets/ab4f6b9519794522aa6ffa6c31617bf8_0.geojson'
#result = JSON.parse open(url).read
end
This my index view:
<% #result.each do |row| %>
<%= row %>
<% end %>

Given that the API (as you are currently using it) returns a JSON structure like this:
{
"type":"FeatureCollection",
"features":[
{
"type":"Feature",
"properties":{
"FIELDNAME":"GRYPHON",
"FIELDTYPE":"OIL",
"NAME_SHORT":"GRYPHON",
"STATUS":"PRODUCING",
"DISC_DATE":"1987/07",
"DISC_WELL":"9/18b-7",
"STAT_CODE":"800",
"PROD_DATE":"1993/10",
"DEPTH_M":"111.86",
"DET_STATUS":"DETERMINED",
"ORIG_OP":"KERR-MCGEE",
"ORIG_LIC":"P.496",
"ORIG_LIC_2":"P.478",
"ORIG_LIC_3":"P.257",
"ORIG_LIC_4":"P.103",
"ORIG_LIC_5":" ",
"CURR_OPER":"MAERSK OIL NORTH SEA UK LIMITED",
"FIELDDATA":"https://itportal.decc.gov.uk/fields/fields_index/ukcs+field+information+listed+by+field+name/183.htm",
"OBJECTID":16,
"OGA_COP":null
},
"geometry":{
"type":"Polygon",
"coordinates":[
[
[1.5701447246411744,59.35253688325039],
...
]
]
}
},
...
]
}
You could do something like:
<% #result[:features].each do |feature| %>
<%= feature[:properties][:FIELDNAME] %>
<%= feature[:properties][:FIELDTYPE] %>
...
<% end %>
Your JSON file looks to be something like 1.3MB. So, unless you can figure out how to filter your results on the API side (using query params, I would suppose), you may end up with various performance issues in retrieving the JSON.
And, you may want to do:
#result = JSON.parse(open(url).read).with_indifferent_access
So that you can use symbols to access hash elements as well as strings.

One thing to add to #jvillian answer is that if one of the keys is nil then calling this branch's subsequent keys will result in undefined method '[]'. Ruby 2.3+ has a new method called dig which will simply return nil. More on dig in my answer to this question.

Also to add to #jvillian answer, you can fetch the filtered information using this link; for example, taking into account the fields you need:
FIELDNAME
FIELDTYPE
STATUS
DISC_DATE
DISC_WELL
You could create query that will result in the following response:
{
"displayFieldName": "FIELDNAME",
"fieldAliases": {
"FIELDNAME": "Field Name",
"FIELDTYPE": "Field Type",
"STATUS": "Status",
"DISC_DATE": "Discovery Date",
"DISC_WELL": "Discovery Well"
},
"fields": [
{
"name": "FIELDNAME",
"type": "esriFieldTypeString",
"alias": "Field Name",
"length": 32
},
{
"name": "FIELDTYPE",
"type": "esriFieldTypeString",
"alias": "Field Type",
"length": 4
},
{
"name": "STATUS",
"type": "esriFieldTypeString",
"alias": "Status",
"length": 50
},
{
"name": "DISC_DATE",
"type": "esriFieldTypeString",
"alias": "Discovery Date",
"length": 20
},
{
"name": "DISC_WELL",
"type": "esriFieldTypeString",
"alias": "Discovery Well",
"length": 20
}
],
"features": [
{
"attributes": {
"FIELDNAME": "GRYPHON",
"FIELDTYPE": "OIL",
"STATUS": "PRODUCING",
"DISC_DATE": "1987/07",
"DISC_WELL": "9/18b-7"
}
},
{
"attributes": {
"FIELDNAME": "BRAEMAR",
"FIELDTYPE": "COND",
"STATUS": "PRODUCING",
"DISC_DATE": "1995/05",
"DISC_WELL": "16/03b-8Z"
}
},
...
]
}
This file will be now 76KB, and you can extract the data in almost the same way, just change properties for attributes, i.e.:
<% #result[:features].each do |feature| %>
<%= feature[:attributes][:FIELDNAME] %>
<%= feature[:attributes][:FIELDTYPE] %>
...
<% end %>

Related

Rails 5: How do I loop over a hash with each do

I want to get a specific output from the Typeform API.
This is the response I get back.
Example response:
"answers": [
{
"field": {
"id": "hVONkQcnSNRj",
"type": "dropdown",
"ref": "my_custom_dropdown_reference"
},
"type": "text",
"text": "Job opportunities"
},
{
"field": {
"id": "RUqkXSeXBXSd",
"type": "yes_no",
"ref": "my_custom_yes_no_reference"
},
"type": "boolean",
"boolean": false
}
]
Why does .first work and why does .second not work ?
My OrdersController.rb
items = response.parsed_response["items"]
items.each do |item|
#order = current_user.orders.find_or_create_by(landing_id: item["landing_id"]) do |order|
item["answers"].each do |answer|
order.landing_id = item["landing_id"]
order.email = item["hidden"]["email"]
order.price = item["hidden"]["price"]
order.moduls = item["hidden"]["moduls"]
order.project = item["hidden"]["project"]
order.website = answer.first # This works
order.payment = answer.second # undefined method `second' for #<Hash:0x11f83e78>
end
end
end
You can do
answers.each { |answer| answer[:field] }
or, if you want ids for example
answers.map { |answer| answer.dig(:field, :id) }
Because ruby hash doesn't have any second or last methods. You can access value with the help of keys. e.g. answer[:type], answer[:text]
item["answers"].each do |answer| was an overkill. The solution was as simple as that:
order.website = item["answers"][1]["text] # Access the first field of answers array
order.payment = item["answers"][2]["text] # Access the second field of answers array

Rails merge multiple json response

Can anyone help me with this problem?
So, here is the problem, I want to merge this query response:
#energy = Alert.where(["alert_type = ?", "Energy"]).last.as_json
#cost = Alert.where(["alert_type = ?", "Cost"]).last.as_json
Then I merge those object with:
#current_notif = #energy.merge(#cost)
But those just give me #cost object like this:
{
"alert_type": "Cost",
"value": 30000000,
"status": "Cost exceeds limit",
"created_at": "2017-06-03T15:31:21.156+07:00",
"updated_at": "2017-06-03T15:31:21.156+07:00",
"home_id": 2
}
Rather than a merged #energy + #cost like this:
{ {"alert_type": "Energy",
"value": 384455.813978742,
"status": "Energy too high",
"created_at": "2017-05-31T11:31:12.907+07:00",
"updated_at": "2017-05-31T11:31:12.907+07:00",
"home_id": 2 },
{
"alert_type": "Cost",
"value": 30000000,
"status": "Cost exceeds limit",
"created_at": "2017-06-03T15:31:21.156+07:00",
"updated_at": "2017-06-03T15:31:21.156+07:00",
"home_id": 2
}
}
If you want you could "join" both values, and then over that use as_json:
[#energy, #cost].as_json
# [{"alert_type": "Energy", ... }, {"alert_type": "Cost", ... }]
Or if you want you could use the IN expression, in order to deal with ActiveRecord instead having to customize the result this gives you:
Alert.where(alert_type: ['Energy', 'Cost']).as_json
# [{"alert_type": "Energy", ... }, {"alert_type": "Cost", ... }]
This is happening because that's how merge works.
hash = {:name => "Ade", :gender => "Male"}.merge(:name => "Bob")
puts hash # {:name=>"Bob", :gender=>"Male"}
Solution:
results = [ #energy, #cost ]
results.each do |result|
puts result['alert_type'] # Energy, Cost
end

Rails 4 - Iterate through nested JSON params

I'm passing nested JSON into rails like so:
{
"product": {
"vendor": "Acme",
"categories":
{
"id": "3",
"method": "remove",
},
"categories":
{
"id": "4"
}
}
}
in order to update the category on a product. I am trying to iterate through the categories attribute in my products_controller so that I can add/remove the product to multiple categories at once:
def updateCategory
#product = Product.find(params[:id])
params[:product][:categories].each do |u|
#category = Category.find_by(id: params[:product][:categories][:id])
if params[:product][:categories][:method] == "remove"
#product.remove_from_category(#category)
else
#product.add_to_category(#category)
end
end
end
However, this only uses the second 'categories' ID in the update and doesn't iterate through both.
Example response JSON:
{
"product": {
"id": 20,
"title": "Heavy Duty Aluminum Chair",
"product_price": "47.47",
"vendor": "Acme",
"categories": [
{
"id": 4,
"title": "Category 4"
}
]
}
}
As you can see, it only added the category with ID = 4, and skipped over Category 3.
I'm fairly new to rails so I know I'm probably missing something obvious here. I've played around with the format of the JSON I'm passing in as well but it only made things worse.
You need to change your JSON structure. As you currently have it, the second "categories" reference will override the first one since you can only have 1 instance of a key. To get what you want, you should change it to:
{
"product": {
"vendor": "Acme",
"categories": [
{
"id": "3",
"method": "remove",
},
{
"id": "4"
}
]
}
}
You will also need to change your ruby code to look like:
def updateCategory
#product = Product.find(params[:id])
params[:product][:categories].each do |u|
#category = Category.find_by(id: u[:id])
if u[:method] == "remove"
#product.remove_from_category(#category)
else
#product.add_to_category(#category)
end
end
end

Rails Backbone Render Nested JSON in jst.eco template

I have this set JSON data
JSON
[{
"country": {
"name": "Malaysia",
"total_amount": 0.0,
"count": 0
}
}, {
"country": {
"name": "Philippines",
"total_amount": 0.0,
"count": 0
}
}, {
"country": {
"name": "Thailand",
"total_amount": 0.0,
"count": 0
}
}]
Let say this data I sent it like
.replaceWith(#template(data: #data_transaction)
How do I retrieve the data inside the template.jst.eco
I have tried to use this kind of for loop
<% for key, data in #data_transaction.models: %>
<%= data.get("country").name %>
<%= data.get("country").total_amount %>
<%= data.get("country").count %>
it just does not work
if I tried to print it out this way inside the template.jst.eco
<%= #data_transaction %>
it will show this kind of things
[object, object]
Any helps?
Thank you very much
I fear you made a very common mistake.
Try to replace:
for key, data in #data_transaction.models:
with:
for object in #data_transaction.models:
for key, data of object:

Displaying an object with Ruby on Rails

I have some JSON that looks like this. I have it stored and read into an object, #items.
[
{
{
"id": "A",
"description": "a_description"
},
{
"id": "B",
"description": "b_description"
}
},
{
{
"id": "A",
"description": "a_description"
},
{
"id": "B",
"description": "b_description"
}
}
]
My goal is to display a table with two columns, one labeled A and the other labeled B, in which each row gives the "a_description" and "b_description". I'm not sure how to go about doing this.
Ah, the ol' array of hashes and hashes of arrays problem.
To get around your "out of order" problem you first have to convert
{
"id": "A",
"description": "foo"
},
{
"id": "B",
"description": "bar"
}
into {"A" : "foo", "B" : "bar" }.
#new_items = #items.map do |item|
output = {}
item.each do |hash|
output.merge!(hash["id"] => hash["description"])
end
end
Then #new_items becomes (intentionally presented out of order since hash elements are not ordered)
[
{
"A": "a1_description",
"B": "b1_description"
},
{
"B": "b2_description",
"A": "a2_description"
}
]
From there, each line is simply a hash, so you can just dereference the value you need based on the column you're in.
#new_items.each do |item|
puts "#{item['A']} is paired with #{item['B']}"
end
Keys, of course could be retrieved dynamically if you don't want to hard code "A" and "B" using .keys
Something like this maybe
<tr><th>A</th><th>B</th></tr>
<% #items.each do |item| %>
<tr><td><%=item[0].description%></td><td><%=item[1].description%></td></tr>
<% end %>

Resources