JBuilder loop that produces hash - ruby-on-rails

I need loop that produces hash, not an array of objects. I have this:
json.service_issues #service.issues do |issue|
json.set! issue.id, issue.name
end
that results:
service_issues: [
{
3: "Not delivered"
},
{
6: "Broken item"
},
{
1: "Bad color"
},
{
41: "Delivery problem"
}
]
I need this:
service_issues: {
3: "Not delivered",
6: "Broken item",
1: "Bad color",
41: "Delivery problem"
}
Is it possible to do this without converting AR result to hash manually?

Jbuilder dev here.
Short answer: Yes. It's possible without converting array of models into hash.
json.service_issues do
#service.issues.each{ |issue| json.set! issue.id, issue.name }
end
but it'd probably be easier to prepare hash before-hand.
json.service_issues Hash[#service.issues.map{ |issue| [ issue.id, issue.name ] }]

For anyone who is interested in having an hash of arrays (objects), you can use the following code:
#bacon_types.each do |bacon_type|
json.set! bacon_type.name, bacon_type.bacons do |bacon|
bacon.title bacon.title
...
end

You can do it like this way
Jbuilder.encode do |json|
json.service_issues #service.issues.inject({}) { |hash, issue| hash[issue.id] = issue.name; hash }
end
The code generating hash technique may be understood by following example.
[1] pry(main)> array = [{id: 1, content: 'a'}, {id: 2, content: 'b'}]
=> [{:id=>1, :content=>"a"}, {:id=>2, :content=>"b"}]
[2] pry(main)> array.inject({}) { |hash, element| hash[element[:id]] = element[:content]; hash }
=> {1=>"a", 2=>"b"}
The key point of inject to generate hash, return created hash every after inserting new element. Above example, it is realized by ; hash.

Related

Sort items in a nested hash by their key

I have a nested hash with unsorted keys:
given = {
"lorem" => {
:AA => "foo",
:GR => "foo",
:BB => "foo"
},
"ipsum" => {
:ZZ => "foo",
:GR => "foo",
}
}
What I'm trying to accomplish is a hash with sorted keys:
goal = {
"ipsum" => {
:GR => "foo",
:ZZ => "foo"
},
"lorem" => {
:AA => "foo",
:BB => "foo",
:GR => "foo"
}
}
I have experimented with .each method and sort_by
given.each { |topic| topic[:key].sort_by { |k, v| k } }
But I'm getting an error message: TypeError: no implicit conversion of Symbol into Integer
Any help is greatly appreciated!
PS: I noticed with gem pry the output is already sorted. But in IRB it's not.
You can use group_by, and transform_values to transform the values inside each hash, also using sort_by plus to_h:
given.transform_values { |value| value.sort.to_h }.sort.to_h
# {"ipsum"=>{:GR=>"foo", :ZZ=>"foo"}, "lorem"=>{:AA=>"foo", :BB=>"foo", :GR=>"foo"}}
You're getting an error because when iterating over a hash, you have to local variables within the block scope to use, the key and its value, you're assigning only one (topic) and trying to get its key, which would be trying to access a key in:
["lorem", {:AA=>"foo", :GR=>"foo", :BB=>"foo"}]
Which isn't possible as is an array. You can update your code to:
given.each do |topic, value|
...
end
But anyway you'll need a way to store the changes or updated and sorted version of that topic values.
given_hash = {"lorem"=>{:AA=>"foo", :GR=>"foo", :BB=>"foo"}, "ipsum"=>{:ZZ=>"foo", :GR=>"foo"}}
Get keys
given_hash.keys
=> ["lorem", "ipsum"]
New sorted hash
new_hash = {}
given_hash.keys.sort.each do |sorted_key|
new_hash[sorted_key] = given[sorted_key]
end
=> {"ipsum"=>{:ZZ=>"foo", :GR=>"foo"}, "lorem"=>{:AA=>"foo", :GR=>"foo", :BB=>"foo"}}
There can be a better way to do this.

how to create an array of hashes by looping over array of objects

I have following array of hash. I am trying to loop over it and build an array of hash of values of id and product_order_id.
objects =
[
#<Product: 0x00007ffd4a561108
#id="1",
#product_id="2",
#product_order_id="23",
#description="abc",
#status="abcde",
#start_date=nil,
#end_date=nil>,
#<Product: 0x00007ffd4a560c80
#id="45",
#product_id="22",
#product_order_id="87",
#description="ahef",
#status="gesff",
#start_date=nil,
#end_date=nil>
......more objects.....
]
This is what it should look like
[{ "1": "23" }, { "45": "87" }] -->its going to be uuid
I tried doing this but no luck
def mapped_product(objects)
mapping = []
objects.each do |object|
mapping << {
object.product_order_id: object.id
}
end
end
Any idea?
inline solution:
> Hash[objects.map{|p| [p.id, p.product_order_id] }]
# Output : [{ 1=>23 }, { 45=>87 }]
I'd usually implement it using an each_with_object
objects.each_with_object({}) { |obj, acc| acc[obj.id] = obj.product_order_id }
Unless I reaaaly want to squeeze some performance, than I'd go with Gagan's answer
Have you tried this?
def mapped_product(objects)
mapping = []
objects.each do |object|
mapping << {
object.id => object.product_order_id # I'm using an `=>` here
}
end
mapping # return the new mapping
end
I've just changed the : on the hash for a => to "make it dynamic" and swapped the values of id and product_order_id
You can also use a map here:
def mapped_product(objects)
objects.map do |object|
{ object.id => object.product_order_id }
end
end

Rails merge multiple object into one array

I am creating API. Using ActiveRecords. Problem I am getting
Multiple array object of country, all I want one array containing all location
Current Output
{
"id": "180a096",
"country": [
{
"location": "US"
},
{
"location": "CH"
}
]
}
Expected Output
{
"id": "180a096",
"country": [
{"location":["US","CH"]}
]
}
Code
def as_json(options={})
super(:only => [:id ],:include => { :country => { :only => :location } })
end
Can anyone help me to restructured the object as in expected output.
If your hash is called hash you can do:
hash[:country].map {|h| h[:location]}
If you have to access attributes on associated models you can do:
countries.pluck(:location)
Unrelated to the question, but when I have to manage country info in my app I tend to use the countries gem. https://github.com/hexorx/countries
It has all kinds of useful helper methods, and it prevents you from having to maintain standardized country information.
You can simply map all the location and assign it to hash[:country]
2.4.0 :044 > hash[:country].map! { |c| c[:location] }
=> ["US", "CH"]
2.4.0 :045 > hash
=> {:id=>"180a096", :country=>["US", "CH"]}
As mentioned in my comment, you can do in one line like
actual_hash[:country].map! { |country| country[:location]}
actual_hash # => {:id=>"180a096", :country=>["US", "CH"]}
The output is clean but not as expected.
Or, a bit more lines to get the exact output:
location_array = [{location: []}]
actual_hash[:country].each { |country| location_array[0][:location] << country[:location]}
actual_hash[:country] = location_array
actual_hash # => {:id=>"180a096", :country=>[{:location=>["US", "CH"]}]}
def rearrange_json(input)
input_hash = JSON.parse(input)
output_hash = input_hash.clone
output_hash[:country] = {location: []}
input_hash[:country].map {|l| output_hash[:country][:location] << l[:location] }
output_hash.as_json
end
With this method, you can convert your json to a hash, then rearrange its content they way you want by adding the country codes as values for the [:country][:location] key of the output hash, and end up with some properly formatted json. It's not a one-liner, and probably not the most elegant way to do it, but it should work.

Rails iterate multiple hash

I have the following hash :
{
"2017-01-01" => {
"2"=> [
{:a=>"2017-01-01", :b=>"2", :c=>"1"},
{:a=>"2017-01-01", :b=>"2", :c=>"2"}
]
},
"2017-01-02" => {
"5"=> [
{:a=>"2017-01-02", :b=>"5", :c=>"1"}
]
}
}
I would iterate separately
1)first iteration
{
{:a=>"2017-01-01", :b=>"2", :c=>"1"},
{:a=>"2017-01-01", :b=>"2", :c=>"2"}
}
2) second iteration
{
{:a=>"2017-01-02", :b=>"5", :c=>"1"}
}
How can I do? Thanks in advance.
answer for your question is in How to iterate over a hash in Ruby?
check it.
hash.each do |key, array|
puts array
end
if 'array' again is a hash, then you need to loop it as follows
hash.each do |key, hash2|
hash2.each do |key2,array|
puts array
end
end

Remove attribute from object recursivly

I got a JSON string, which I would like to strip from some values. The problem is that the JSON object can contain child objects, which if they exist, I want to strip of the same sort of values (based on the key).
For example, I got this:
{
Title: "test",
Created: "2013-01-01",
ID: 1
Child: {
Title: "Test 2",
Created: "2013-01-02",
ID: 2,
RandomName: {
Title: "Test 3",
Created: "2013-01-05",
ID:3
}
}
}
I would like to remove the key "Created" from the objects and from all the child objects. Is there an easy way to achieve this in Ruby?
You can write a helper that calls the nested element recursively (example assuming you parsed the JSON to a hash)
def remove_recursive(hash)
hash.each do |key, value|
hash.delete(key) if key == "Created"
remove_recursive(hash[key]) if hash[key].kind_of?(Hash)
end
end
You can do it with a proc if you don't want to have a method just for this.
require 'json'
json_string = '{
"Title": "test",
"Created": "2013-01-01",
"ID": 1,
"Child": {
"Title": "Test 2",
"Created": "2013-01-02",
"ID": 2,
"RandomName": {
"Title": "Test 3",
"Created": "2013-01-05",
"ID": 3
}
}
}'
without_fields = proc do |h, *fields|
h = h.reject {|k,_| fields.include?(k) }
h.each do |k, v|
if v.is_a?(Hash)
h[k] = without_fields.call(v, *fields)
end
end
h
end
json_obj = JSON.load(json_string)
cleaned_obj = without_fields.call(json_obj, 'Created')
JSON.dump(cleaned_obj)
# => "{\"Title\":\"test\",\"ID\":1,\"Child\":{\"Title\":\"Test 2\",\"ID\":2,\"RandomName\":{\"Title\":\"Test 3\",\"ID\":3}}}"
Based on Tobias' answer, I modified the code to support child arrays etc. too. Mind you, I am fairly new to Ruby, so I am not sure this is a 100% complete.
The keysToRemove parameter is an array which contains the names of the keys I want to remove.
def remove_recursive(hash, keysToRemove)
if hash.kind_of?(Array)
hash.each do |h|
remove_recursive(h, keysToRemove)
end
elsif hash.kind_of?(Hash)
hash.each do |key, value|
if keysToRemove.include?(key)
hash.delete(key)
else
remove_recursive(hash[key], keysToRemove)
end
end
end
end

Resources