parsing json object and accessing a specific element - ruby-on-rails

I am trying to parse this json object.
{"coord"=>{"lon"=>-0.13, "lat"=>51.51}, "sys"=>{"message"=>0.2063, "country"=>"GB", "sunrise"=>1387958729, "sunset"=>1387986980}, "weather"=>[{"id"=>801, "main"=>"Clouds", "description"=>"few clouds", "icon"=>"02n"}], "base"=>"gdps stations", "main"=>{"temp"=>276.49, "pressure"=>983, "temp_min"=>275.37, "temp_max"=>277.59, "humidity"=>95}, "wind"=>{"speed"=>0.51, "gust"=>1.54, "deg"=>229}, "rain"=>{"3h"=>0}, "clouds"=>{"all"=>24}, "dt"=>1387950679, "id"=>2643743, "name"=>"London", "cod"=>200}
Here is my code:
class WeatherController < ApplicationController
def index
require 'json'
response = HTTParty.get('http://api.openweathermap.org/data/2.5/weather?q=London,uk')
response.each do |key, value|
puts "key = #{key}, value = #{value}"
end
response.each do |item|
puts item
end
end
end
The two loops work and go through the object. In the item loop shouldn't I be able to do something like puts item.coord to just get that element?

I would use directly response['coord'] in your code, you don't need to do any looks. And you can check if its nil and handle this situation

Related

How can I remove the data in array have used group_by method in rails?

I want to remove the value of data have been group_by, because the object #deals
I need to arrange the data by date.
But, the problems is that I used iteration of #period and the value in the #deals would be calculated once again.It's not efficient and I tried to use map method to slice the data as following:
#period = Date.new(2019,3,1)..Date.new(2019,3,31)
#deals = Deal.where(created_at: #period).includes(:staff).group_by{|t| t.staff}
#period.each do |date|
#deals.map do |staff, deals|
puts "staff:#{staff.name}"
puts deals.count
selected_deals = deals.select{|t| Date.parse(t.created_at) == date}
puts selected_deals.select{|t| t.deal_product=="item1"}.count
puts selected_deals.select{|t| t.deal_product=="item2"}.count
puts selected_deals.select{|t| t.deal_product=="item3"}.count
puts selected_deals.select{|t| t.deal_product=="item4"}.count
deals-selected_deals
end
end
but it's seen not remove the data I have selected.
It's possibile to do what I want?
.select doesn't change data in place, but returns a new value, so does .map. Try #period.map and assign whole expression to a variable.
#data = #period.map do |date|
#deals.map do |staff, deals|
selected_deals = deals.select{|t| t.created_at.to_date == date}
deals - selected_deals
end
end
If you want to reject values from array, use .reject.
#data = #period.map do |date|
#deals.map do |staff, deals|
deals.reject{|t| t.created_at.to_date == date}
end
end

Handle bad JSON gracefully with Hash#fetch

I'm trying to fetch some products from this JSON API so I can display them in my views, with bad JSON gracefully handled using Hash#fetch to declare a default value if I get nil.
But why am I getting:
can't convert String into Integer
or if I set #json_text to {}:
undefined method 'fetch' for `nil:NilClass`
Live app: http://runnable.com/U-QEWAnEtDZTdI_g/gracefully-handle-bad-json
class MainController < ApplicationController
require 'hashie'
def index
#json_text = <<END
{
"products": []
}
END
hashes = JSON.parse(#json_text)
mashes = Hashie::Mash.new(hashes)
products = []
mashes.products.fetch('products', []).each do |mash|
puts "YOLO"
end
end
end
The right way to fetch is by calling fetch on mashes variable:
mashes.fetch('products', []).each do |mash|
puts "YOLO"
end
#fetch is a Hash method and in this case is the same as mashes['product'], except when product is nil, using the fetch example will return []

Clear all values in nested ruby hash

How can I remove all values from ruby has. I don't want to remove keys just values.
For example:
here is my hash: {'a'=>{'b'=>'c'},'d'=>'e','f'=>{'g'=>''}}
I want this: {'a'=>{'b'=>nil},'d'=>nil,'f'=>{'g'=>nil}}
I don't want to delete the nested hashes. The nesting level varies from one to six levels
thanx
You can write custom delete_values! method, like this:
class Hash
def delete_values!
each_key do |key|
self[key].is_a?(Hash) ? self[key].delete_values! : self[key] = nil
end
end
end
{'a'=>{'b'=>'c'},'d'=>'e','f'=>{'g'=>''}}.delete_values!
# => {"a"=>{"b"=>nil}, "d"=>nil, "f"=>{"g"=>nil}}
h = {'a'=>{'b'=>'c'},'d'=>'e','f'=>{'g'=>''}}
def clean_hash h
h.each do |key, value|
if value.instance_of? Hash
clean_hash value
else
h[key] = nil
end
end
end
clean_hash h
#{"a"=>{"b"=>nil}, "d"=>nil, "f"=>{"g"=>nil}}
h = {'a'=>{'b'=>'c'},'d'=>'e','f'=>{'g'=>''}}
def cleaned_hash(h)
h.reduce({}) do |memo, (key, val)|
memo[key] = if val.is_a? Hash
cleaned_hash(val)
else
nil
end
memo
end
end
cleaned_hash h
# => {"a"=>{"b"=>nil}, "d"=>nil, "f"=>{"g"=>nil}}
This will not modify your hash but instead give you cleaned copy

Ruby: How to make custom DSL accept variables

I have this class:
class Items
def initialize &block
(block.arity < 1 ? (instance_eval &block) : block.call(self)) if block_given?
end
def button_id button_id=nil
unless #button_id.present?
raise "button_id must be supplied" if button_id.nil?
#button_id = button_id
end
#button_id
end
end
Now, when I do this it works:
Items.new do
button_id 1
end
But when I do this, it fails because I think it is not on the same scope:
#button = Button.find(params[:button_id]
Items.new do
button_id #button.id
end
How can fix this to take arguments outside the scope?
Thanks!
Try this:
class Items
def self.dsl
new.tap do |item|
yield item
end
end
def button_id(button_id)
#button_id = button_id
end
end
#button = Button.find(params[:button_id])
item = Items.dsl do |item|
item.button_id(#button.id)
end
puts item.inspect
Turns out all I needed to do was to pass the arguments to the block like this:
Items.new do |item|
item.button_id #button.id
end
Less beautiful DSL but works.
I don't think this is the right use case of DSL, when you can simply assign the attributes by arguments.
class Item
attr_accessor: :button_id
def initialize(args)
button_id = args[:button_id]
end
end
Another problem is in your usage. The instance would be of little value if you don't assign it to a variable
item = Item.new button_id: button_id

TypeError: no implicit conversion of Symbol into Integer

I encounter a strange problem when trying to alter values from a Hash. I have the following setup:
myHash = {
company_name:"MyCompany",
street:"Mainstreet",
postcode:"1234",
city:"MyCity",
free_seats:"3"
}
def cleanup string
string.titleize
end
def format
output = Hash.new
myHash.each do |item|
item[:company_name] = cleanup(item[:company_name])
item[:street] = cleanup(item[:street])
output << item
end
end
When I execute this code I get: "TypeError: no implicit conversion of Symbol into Integer" although the output of item[:company_name] is the expected string. What am I doing wrong?
Your item variable holds Array instance (in [hash_key, hash_value] format), so it doesn't expect Symbol in [] method.
This is how you could do it using Hash#each:
def format(hash)
output = Hash.new
hash.each do |key, value|
output[key] = cleanup(value)
end
output
end
or, without this:
def format(hash)
output = hash.dup
output[:company_name] = cleanup(output[:company_name])
output[:street] = cleanup(output[:street])
output
end
This error shows up when you are treating an array or string as a Hash. In this line myHash.each do |item| you are assigning item to a two-element array [key, value], so item[:symbol] throws an error.
You probably meant this:
require 'active_support/core_ext' # for titleize
myHash = {company_name:"MyCompany", street:"Mainstreet", postcode:"1234", city:"MyCity", free_seats:"3"}
def cleanup string
string.titleize
end
def format(hash)
output = {}
output[:company_name] = cleanup(hash[:company_name])
output[:street] = cleanup(hash[:street])
output
end
format(myHash) # => {:company_name=>"My Company", :street=>"Mainstreet"}
Please read documentation on Hash#each
myHash.each{|item|..} is returning you array object for item iterative variable like the following :--
[:company_name, "MyCompany"]
[:street, "Mainstreet"]
[:postcode, "1234"]
[:city, "MyCity"]
[:free_seats, "3"]
You should do this:--
def format
output = Hash.new
myHash.each do |k, v|
output[k] = cleanup(v)
end
output
end
Ive come across this many times in my work, an easy work around that I found is to ask if the array element is a Hash by class.
if i.class == Hash
notation like i[:label] will work in this block and not throw that error
end

Resources