how to merge Hashes in Ruby/Rails - ruby-on-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.

Related

Filter by Array Length in OData

I want to filter based on the length of data. For example, I want to retrieve only the images array that contains more than 1 element. Something like that images.length > 6.
Is this possible using OData. Your help is much appreciated
{
"#search.score": 1,
"site": "core",
"images": [
{
"alt": "Quad"
}
]
}, ,{
"#search.score": 1,
"site": "ccc",
"images": [
{
"alt": "some"
}
]
},
{
"#search.score": 1,
"site": "ccc",
"images": [
{
"alt": "DePaul's Student Center"
},
{
"alt": "O'Malley"
},
{
"alt": "Campus"
},
{
"alt": " Campus"
},
{
"alt": "ith students"
},
{
"alt": "er"
},
{
"alt": "df"
},
{
"alt": "sdf"
},
{
"alt": "df"
},
]
A simpler and better solution is to populate your data with an additional property called ImageCount. If you submit your items via the Azure Search SDK, you can process them before submitting them to search. Add the image count to each item and then filter like normal.
$filter=ImageCount gt 6

Odata filter expression to query for a particular field name in json

I have the following Odata response
{
"d": {
"results": [
{
"__metadata": {
"id": ....,
"URI": .....,
"type": .....
},
"SchemaId": "ABC"
},
{
"__metadata": {
"id": ....,
"URI": .....,
"type": .....
},
"SchemaId": "DEF"
}
]
}
I want to filter for all the schemaId. Can anyone help with filter query.
I want to filter for all the schemaId. -> No idea what you say, but if you want to filter for a SchemaId you have to do it like this:
.../EntitySet$filter=SchemaId eq 'Whateva'
https://www.odata.org/getting-started/basic-tutorial/

Rails - How to add pagination to Fastjson api?

The default result of rendering FastJsonApi gem serialized_json like below:
render json: FlashcardSerializer.new(flashcards).serialized_json
would be something like this:
{
"data": [
{
"id": "1",
"type": "flashcard",
"attributes": {
"question": "why?",
"answer": "pretty good",
"slug": null
}
},
{
"id": "2",
"type": "flashcard",
"attributes": {
"question": "What is 0",
"answer": "it is 0",
"slug": null
}
}
]
}
I would rather add some extra information especially for pagination and I like the result to be something like this:
{
"data": [
{
"id": "1",
"type": "flashcard",
"attributes": {
"question": "why?",
"answer": "pretty good",
"slug": null
}
},
{
"id": "2",
"type": "flashcard",
"attributes": {
"question": "What is 0",
"answer": "it is 0",
"slug": null
}
},
"count":100,
"page":1,
]
}
I am aware of other available gems that manage pagination in API, and I know how to do it without Fastjson. The main issue here is that is there any way to get the aforementioned result from this gem without changing a lot in the code. Thanks
The desired document would be invalid according to the JSON API specification. You would need to include next and previous links in a link section. The current and total_count would belong in the meta section.
{
"data": [
{
"id": "1",
"type": "flashcard",
"attributes": {
"question": "why?",
"answer": "pretty good",
"slug": null
}
},
{
"id": "2",
"type": "flashcard",
"attributes": {
"question": "What is 0",
"answer": "it is 0",
"slug": null
}
},
]
"meta": {
"page": { "current": 1, "total": 100 }
},
"links": {
"prev": "/example-data?page[before]=yyy&page[size]=1",
"next": "/example-data?page[after]=yyy&page[size]=1"
},
}
Have a look at the JSON API specification before you continue designing the API.
You can pass these information into the serializer as an options argument
class FlashcardsController < ApplicationController
def index
render json: FlashcardSerializer.new(
flashcards, { links: {}, meta: { page: { current: 1 } }
).serialized_json
end
end
How you generate the data depends what you use to paginate.
If you design a new API, I would also recommend to use cursor based pagination rather than offset pagination because of it's limitations.
https://github.com/Netflix/fast_jsonapi#compound-document
https://github.com/Netflix/fast_jsonapi/blob/master/spec/lib/object_serializer_spec.rb#L8-L32

fill a JSON file with embedded arrays from Rails

I have a simple "rss" (ApplicationRecord) table indexed by an id. I would like to have a structured JSON that group each user from a family in an array structure. And then each family in a global array. How can I do that ?
my current plain code to put my data in a json file is :
json.rss #rss do |rs|
json.id rs.id
json.name rs.name
json.family rs.family
json.lastdate rs.lastdate
json.last rs.last
json.s1w rs.s1w
json.s2w rs.s2w
end
But the target file that I want is this one :
{
"rss": [
{
"familyname": "Smith",
"children": [
{
"id": "1",
"name": "bob",
"lastdate": "2010-09-23",
"last": "0.88",
"s1w": "0.83",
"s2w": "0.88"
},
{
"id": 2,
"name": "Mary",
"lastdate": "2011-09-23",
"last": "0.89",
"s1w": "0.83",
"s2w": "0.87"
}
]
},
{
"familyname": "Wesson",
"children": [
{
"id": "1",
"name": "john",
"lastdate": "2001-09-23",
"last": "0.88",
"s1w": "0.83",
"s2w": "0.88"
},
{
"id": 2,
"name": "Bruce",
"lastdate": "2000-09-23",
"last": "0.89",
"s1w": "0.83",
"s2w": "0.87"
}
]
}
]
}
The grouping you are trying to achieve can be done in Ruby with:
#rss.group_by(&:family).values
This is assuming #rss is an array-like collection of objects that have a .family method. The result: is an array of arrays of objects grouped by family.
Now it will be up to use to use Jbuilder's array! method to build the desired JSON output.

Converting JSON number to_i returning 1

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.

Resources