Converting JSON number to_i returning 1 - ruby-on-rails

I've been given this hash:
{
"item": {
"icon": "http://services.runescape.com/m=itemdb_rs/4332_obj_sprite.gif?id=4798",
"icon_large": "http://services.runescape.com/m=itemdb_rs/4332_obj_big.gif?id=4798",
"id": 4798,
"type": "Ammo",
"typeIcon": "http://www.runescape.com/img/categories/Ammo",
"name": "Adamant brutal",
"description": "Blunt adamantite arrow...ouch",
"current": {
"trend": "neutral",
"price": 227
},
"today": {
"trend": "neutral",
"price": 0
},
"day30": {
"trend": "positive",
"change": "+1.0%"
},
"day90": {
"trend": "positive",
"change": "+1.0%"
},
"day180": {
"trend": "positive",
"change": "+2.0%"
},
"members": "true"
}
}
I obtain the current price like this:
class GpperxpController < ApplicationController
def index
end
def cooking
require 'open-uri'
#sharkid = '385'
#sharkurl = "http://services.runescape.com/m=itemdb_rs/api/catalogue/detail.json?item=#{#sharkid}"
#sharkpage = Nokogiri::HTML(open(#sharkurl))
#sharkinfo = JSON.parse(#sharkpage.text)
#sharkinfo = #sharkinfo['item']['current']['price']
end
end
<%= #sharkinfo %> in my view returns 227. However, I want to perform some math operations on it, which is why I must use .to_i. Only problem is when I append .to_i, the value changes to 1. Why is that?

Price in the given json (http://services.runescape.com/m=itemdb_rs/api/catalogue/detail.json?item=385) contains ,.
... "current":{"trend":"neutral","price":"1,844"},...
^
Remove , before call String#to_i.
"1,844".to_i
# => 1
"1,844".gsub(',', '').to_i
# => 1844

Just running irb, and putting your JSON response in a variable, I had no problem getting the response to be 227, either by pulling the price out as text and then converting to an integer or by pulling the price out as an integer in one fell swoop.
So my initial code looked like:
json_text = '''
{
"item": {
"icon": "http://services.runescape.com/m=itemdb_rs/4332_obj_sprite.gif?id=4798",
"icon_large": "http://services.runescape.com/m=itemdb_rs/4332_obj_big.gif?id=4798",
"id": 4798,
"type": "Ammo",
"typeIcon": "http://www.runescape.com/img/categories/Ammo",
"name": "Adamant brutal",
"description": "Blunt adamantite arrow...ouch",
"current": {
"trend": "neutral",
"price": 227
},
"today": {
"trend": "neutral",
"price": 0
},
"day30": {
"trend": "positive",
"change": "+1.0%"
},
"day90": {
"trend": "positive",
"change": "+1.0%"
},
"day180": {
"trend": "positive",
"change": "+2.0%"
},
"members": "true"
}
'''
require 'json'
si = JSON.parse(json_text)
And then either of the following:
p = si['item']['current']['price']
price = p.to_i
or
price = si['item']['current']['price'].to_i
put the value of 227 in my price variable.
Something I would avoid if I were you though, is using the same variable name for different things. If what you want to have is the integer price in #sharkinfo, then you would do well to have a temporary name (without the # symbol) to put the price as text in, then assign the integer value to the desired variable.
Try this and see if it helps. I'll try to monitor this for a bit to see if you get anywhere. Also, at the point you pull the text out of the JSON, I believe this ceases to be a JSON problem any longer. Finally, you might include what version of ruby and what platform (Windows/Mac/Linux/etc) you are using.

Related

Vega-Lite Visualization interpreting dates from Google Sheet as long numbers

Pulling data into Google Data Studio from a Google Sheet with dates stored in yyyy-mm-dd format. The dates look correct and calculate correctly with formulas and adjustments everywhere except in a Gantt chart using the Vega-Lite Community Visualization, which shows the date in a long-number format (e.g. 20210520), and is unable to display the data when using "type": "temporal" or using "timeUnit": "utcyearmonthdatehours".
I've ran various tests, including...
Changing the date format for the date columns to plain text, yyyyddmm, yymmdd, yyyy/mm/dd formats.
Replace the current date columns with new columns using the alternate formats in point 1 (above).
Changing the date field formats directly in Google Data Studio to the formats in point 1 (above).
Creating a secondary set of date columns in plain-text using an Arrayformula and Text() function to reformat the actual dates to plain-text.
So far, options 2 & 4 are the only way I've been able to get the gantt to render correctly, reading the data in date format. But option 2 renders the other charts in GDS as unusable, as the other charts cannot translate the plain-text to usable dates.
Option 4 does work, but isn't the ideal route, given the redundant data. I'd prefer to have just 1 column for the Start Date and another for the End Date, rather than 2 columns for both. Feels like I may be missing something obvious here. Is there a way to either properly format the dates in Google Sheets to work properly with both the GDS date fields and Vega-Lite, or is there a way to properly parse the date data in Vega-Lite without needing to use a second set of plain-text columns?
Report replicating the issue: Project Tracking (debug report)
Edit: below is the code for the Vega-lite visualizations using the date fields from Google Sheets, which Vega-lite is not interpreting as dates.
Without timeunit or temporal field type:
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"description": "A bar chart with highlighting on hover and selecting on click. (Inspired by Tableau's interaction style.)",
"config": {
"background": null,
"view": {
"stroke": "transparent"
}
},
"layer": [
{
"layer": [
{
"params": [
{
"name": "grid",
"select": "interval",
"bind": "scales"
}
],
"mark": {
"type": "bar",
"cursor": "pointer",
"tooltip": true,
"point": true,
"cornerRadiusEnd": 5,
"opacity": 0.8
},
"encoding": {
"color": {
"field": "$dimension3",
"title": "$dimension3.name"
}
}
}
],
"encoding": {
"x": {
"field": "$dimension0",
"axis": {
"title": null,
"grid": true
}
},
"y": {
"field": "$dimension1",
"title": "$dimension1.name",
"type": "nominal",
"sort": "x",
"axis": {
"title": null,
"grid": true,
"tickBand": "extent"
}
},
"x2": {
"field": "$dimension2"
},
"yOffset": {
"field": "$dimension3"
}
}
}
]
}
With timeunit and field type temporal:
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"description": "A bar chart with highlighting on hover and selecting on click. (Inspired by Tableau's interaction style.)",
"config": {
"background": null,
"view": {
"stroke": "transparent"
}
},
"layer": [
{
"layer": [
{
"params": [
{
"name": "grid",
"select": "interval",
"bind": "scales"
}
],
"mark": {
"type": "bar",
"cursor": "pointer",
"tooltip": true,
"point": true,
"cornerRadiusEnd": 5,
"opacity": 0.8
},
"encoding": {
"color": {
"field": "$dimension3",
"title": "$dimension3.name"
}
}
}
],
"encoding": {
"x": {
"field": "$dimension0",
"type": "temporal",
"timeUnit": "utcyearmonthdatehours",
"axis": {
"title": null,
"grid": true
}
},
"y": {
"field": "$dimension1",
"title": "$dimension1.name",
"type": "nominal",
"sort": "x",
"axis": {
"title": null,
"grid": true,
"tickBand": "extent"
}
},
"x2": {
"field": "$dimension2"
},
"yOffset": {
"field": "$dimension3"
}
}
}
]
}

how to merge Hashes in Ruby/Rails

I have a Hash like this, which should be "merged" to its uniq nested values
[
{
"slug": "color",
"values": [{ "slug": "amethyst" },
{ "slug": "coral" }],
},
{
"slug": "color",
"values": [{ "slug": "amethyst" }],
},
{
"slug": "power-source",
"values": [{ "slug": "110V"}],
}
]
at the same time it should count the duplicate values but made uniq in an items array:
{ "slug": "color",
"items": [
{
"slug": "amethyst",
"count": 2
},
{
"slug": "coral",
"count": 1
}]
},
{
"slug": "power-source",
"items": [
{
"slug": "110V",
"count": 1
}]
}
]
is there a "Rails method" to achieve this?
Thank you
I think there's nothing built-in in Rails that allows you to get such a custom requirement, but you can achieve it by playing around with different methods and their return values:
data
.group_by { |hash| hash[:slug] }
.transform_values do |values|
values
.flat_map { |vals| vals[:values] }
.group_by { |value| value[:slug] }
.transform_values(&:count)
end.map do |slug, items|
[slug, items.map { |item, count| {slug: item, count: count} }]
end.map { |slug, items| {slug: slug, items: items} }
# [{:slug=>"color",
# :items=>[{:slug=>"amethyst", :count=>2}, {:slug=>"coral", :count=>1}]},
# {:slug=>"power-source", :items=>[{:slug=>"110V", :count=>1}]}]
As you see, you can first group every hash in the array by their slug value, then transform the values that hash contains, mapping and flattening every array by their values key and then grouping to get their total.
After that you can just create the hash with its keys/values you need.
It might simplify the things a bit if you end up with a single hash, whose keys are the "slugs" and contains the items as its values.

Filter empty object in odata

There is an oData service i want to call. We have an model that has an field Parent. The service tells me that the parent is an object with an iv that is an string inside. So we get the following model from the oData service:
{
"total": 0,
"items": [
{
"id": "string",
"data": {
"Title": {
"en": "string",
"nl": "string"
},
"Parent": {
"iv": [
"string"
]
},
}
]
}
Now when we get data back it looks like this:
{
"total": 2,
"items": [
{
"id": "6204c07d-1aef-4bd2-9646-e3ca36c63784",
"data": {
"Title": {
"en": "Test 1",
"nl": "Test 1"
},
"Parent": {
"iv": []
},
},
},
{
"id": "bfd1b084-4166-4fec-9032-08047c8313d2",
"data": {
"Title": {
"en": "Test 2",
"nl": "Test 2"
},
"Parent": {
"iv": [
"6204c07d-1aef-4bd2-9646-e3ca36c63784"
]
},
},
},
}
Now i want to filter on all the items where parent is [] (so no parent selected). I've tried the following filters but all without success
data/Parent/iv eq [] | Returns 0 results
data/Parent/iv eq null | Returns 0 results
data/Parent eq null | Returns 0 results
length(data/Parent/iv) eq 0 | OData operation is not supported
not data/Parent/any() | Any/All may only be used following a collection.
not data/Parent/any() | Any/All may only be used following a collection.
data/Parent/iv/$count eq 0 | Server error
Found that for this case the following works:
empty(data/Parent/iv)

Defining Array of Objects in Swagger Documentation

I'm using swagger for quite a bit now, we have started documenting our code using it, in one place there's an API response which returns multiple objects in the included block.
Example:
{
"data": {
"id": "1",
"type": "schoolPositions",
"attributes": {
"description": "teases the students",
"mustHaves": "principle"
},
"relationships": {
"schoolLocation": {
"data": {
"id": "72",
"type": "schoolLocations"
}
},
"schoolCompensation": {
"data": {
"id": "75",
"type": "schoolCompensations"
}
},
"jobSpecs": {
"data": [
{
"id": "82",
"type": "schoolAttachments"
}
]
}
}
},
"included": [
{
"id": "72",
"type": "schoolLocations",
"attributes": {
"city": "Berhampore",
"state": "West Bengal",
"postalCode": "742101",
"country": "India",
"globalRegionId": 30,
"regionId": 683
}
},
{
"id": "75",
"type": "schoolCompensations",
"attributes": {
"salary": "",
"bonus": "",
"equity": "",
"currencyId": null,
"equityType": "percent",
"salaryDescription": null
}
},
{
"id": "82",
"type": "schoolAttachments",
"attributes": {
"attachmentType": "JobSpecificationAttachmentType",
"fileFileName": "vs.jpg",
"fileContentType": "image/jpeg",
"fileFileSize": 2410039,
"fileUpdatedAt": "2018-12-12T07:06:38Z",
"downloadUrl": "001-vs.jpg?1544598398",
"klass": "SchoolAttachments"
}
}
]
I have wasted an entire day on the internet and documentation trying to document the included part, but I'm going wrong somewhere
response 200 do
key :description, 'School Data'
schema do
property :data do
key :type, :array
items do
key :'$ref', :School
end
end
property :included do
key :type, :array
items do
key :'$ref', :SchoolLocations
key :'$ref', :SchoolCompensations
key :'$ref', :SchoolAttachments
end
end
end
end
This shows only the SchoolAttachments in the included part.
I have tried using allOff but it doesn't work.

Is there a better way to handle an Array response Rails 4

I am integrating with Scalable Press API. I am using HTTparty to handle the api response.
Example request:
curl "https://api.scalablepress.com/v2/products/gildan-sweatshirt-crew"
Example response:
{
"comments": "Generous fit. Soft, sturdy, easy to move around in, all the while looking good.",
"description": "Air Jet Spun Yarn. Double-needle stitching. Set-in sleeves. 1x1 Athletic Rib with Lycra(R). Quarter-turned to eliminate center crease.",
"name": "Gildan Sweatshirt - Crew",
"type": "Garment",
"properties": {
"brand": "Gildan",
"material": "7.75 oz 50% cotton, 50% polyester.",
"style": "18000"
},
"colors": [{
"name": "Kiwi",
"hex": "88b95d",
"images": [{
"url": "http://i1.ooshirts.com/images/lab_shirts/Kiwi-5-R.jpg",
"label": "Right"
}, {
"url": "http://i1.ooshirts.com/images/lab_shirts/Kiwi-5-L.jpg",
"label": "Left"
}, {
"url": "http://i1.ooshirts.com/images/lab_shirts/Kiwi-5-F.jpg",
"label": "Front"
}, {
"url": "http://i1.ooshirts.com/images/lab_shirts/Kiwi-5-B.jpg",
"label": "Back"
}],
"sizes": [
"sml",
"med",
"lrg",
"xlg",
"xxl",
"xxxl",
"xxxxl",
"xxxxxl"
]
}, {
"name": "Irish Green",
"hex": "3da858",
"images": [{
"url": "http://i1.ooshirts.com/images/lab_shirts/Irish-Green-5-F.jpg",
"label": "Front"
}, {
"url": "http://i1.ooshirts.com/images/lab_shirts/Irish-Green-5-L.jpg",
"label": "Left"
}, {
"url": "http://i1.ooshirts.com/images/lab_shirts/Irish-Green-5-R.jpg",
"label": "Right"
}, {
"url": "http://i1.ooshirts.com/images/lab_shirts/Irish-Green-5-B.jpg",
"label": "Back"
}],
"sizes": [
"sml",
"med",
"lrg",
"xlg",
"xxl",
"xxxl",
"xxxxl",
"xxxxxl"
]
}],
"additionalImages": [{
"label": "Front",
"url": "http://i1.ooshirts.com/products/5/front.jpg"
}, {
"label": "Back",
"url": "http://i1.ooshirts.com/products/5/back.jpg"
}, {
"label": "Collar",
"url": "http://i1.ooshirts.com/products/5/collar.jpg"
}],
"image": {
"label": "Catalog",
"url": "http://www.ooshirts.com/products/5/catalog.jpg"
},
"available": true,
"url": "https://api.scalablepress.com/v2/products/gildan-sweatshirt-crew",
"availabilityUrl": "https://api.scalablepress.com/v2/products/gildan-sweatshirt-crew/availability",
"productId": "gildan-sweatshirt-crew"
}
This response isn't too bad, but some products have upwards of 50 colors and each color has around 4 images.
Question:
I am looking to make my code a little better and easier to access the images. Is there a better way to handle the array?
View: <%= image_tag #product['colors'].to_a[0].to_a[2].to_a[1].to_a[1].to_a[0].to_a[1] %>
controller:
def show_product
#product = scalable_press.show_product(params[:product])
end
private
def scalable_press
ScalablePress.new
end
class ScalablePress
include HTTParty
base_uri 'https://api.scalablepress.com'
def initialize
#options = { basic_auth: { password: 'APIKEY' }, verify: false}
end
def products_in_category(category)
response = self.class.get("https://api.scalablepress.com/v2/categories/#{category}", #options)
response["products"]
end
def categories
response = self.class.get("https://api.scalablepress.com/v2/categories", #options)
end
def show_product(product)
response = self.class.get("https://api.scalablepress.com/v2/products/#{product}", #options)
# response["products"]
end
end

Resources