Build hash index from collection in ruby - ruby-on-rails

here is what I"m trying to get from collection of records:
{
"transaction": {
"amount":"45.55",
"currency":"CAD",
},
"detail": {
"0": {
"sku":"Product_id",
},
"1": {
"sku":"Product_id",
},
"2": {
"sku":"Product_id",
}
}
}
I need to loop through order items and build hash with index.
# Order model
order has_many :items, class_name: "LineItem"
Order.column_names
=> ["id", "amount", "currency"]
# LineItem model
LineItem.column_names
=> ["id", "sku", "order_id"]
Here is what I have so far but looks like I can't do this:
{
transaction: {
amount: order.subtotal,
currency: order.currency,
},
detail: {
order.items.each_with_index do |item, index|
index: {
sku: item.sku
}
end
}
}.to_json
So, let's try to build nested hash separately but no luck either.
ha = items.each_with_index do |item, index|
index => Hash.new(sku: item.sku)
end

Here is what I ended up doing
body = {
transaction: {
amount:"45.55",
currency:"CAD",
}
}
items_hash = {}
items.each_with_index do |item, index|
h = { "#{index}" => { sku: item.sku } }
items_hash.merge!(h)
end
details = { "transaction_detail" => items_hash }
hash = hash.merge!(details)
hash.to_json

Related

convert an array of objects (created from OpenStruct) into an array of values

I am learning ruby and I am playing with a sample data. I converted following hash into an array of objects as follows.
class Openstruct
require 'JSON'
require 'ostruct'
HASH = {
items: [
{
health: [
{
goal: [
{
activity: [
{
id: "1A"
},
{
id: "2A"
}
],
id: "GA"
}
],
activity: [
{
id: "1B"
},
{
id: "2B"
}
],
id: "GB"
}
],
goal: [
{
activity: [
{
id: "1C"
},
{
id: "2C"
},
],
id: "3c"
}
],
createdAt: "2018-01-01",
updatedAt: "2018-01-01",
id: "DA"
}
],
}
def self.all
json = HASH.to_json
JSON.parse(json, object_class: OpenStruct)
end
end
Above returns me following result
#<OpenStruct items=
[#<OpenStruct health=
[#<OpenStruct goal=
[#<OpenStruct activity=
[#<OpenStruct id="1A">, #<OpenStruct id="2A">], id="GA">],
activity=[#<OpenStruct id="1B">, #<OpenStruct id="2B">], id="GB">],
goal=[#<OpenStruct activity=[#<OpenStruct id="1C">, #<OpenStruct id="2C">], id="3c">],
createdAt="2018-01-01",
updatedAt="2018-01-01",
id="DA">]>
However, i want to convert the array of object having id's into array of values of ids. e.g [#<OpenStruct id="1A">, #<OpenStruct id="2A">] --> ["1A", "2A"]. so i want the final result as follows:
#<OpenStruct items=
[#<OpenStruct health=
[#<OpenStruct goal=
[#<OpenStruct activity=
["1A","2A"], id="GA">],
activity=["1B", 2B"], id="GB">],
goal=[#<OpenStruct activity=["1C","2C"], id="3c">],
createdAt="2018-01-01",
updatedAt="2018-01-01",
id="DA">]>
Does anyone know how to do that?
You will have to check the child node recursively until you find the OpenStruct with only :id element. Following is the working code for your sample data.
def self.convert_struct_id(os)
# Get possible attributes of any open_struct
attributes = os.to_h.keys
# Only get id_value if :id is the only attribute of open_struct
if attributes.length == 1 && attributes.first == :id
id_value = os.send(:id)
return id_value
end
# Iterate through attributes
attributes.each do |attr|
# Get child elements
data = os.send(attr)
case data
when OpenStruct
convert_struct_id(data)
when Array
# Recursively process for child node
data.map! { |d| convert_struct_id(d) }
end
end
return os
end
Your self.all method will look like this
def self.all
json = HASH.to_json
os = JSON.parse(json, object_class: OpenStruct)
res = convert_struct_id(os)
end
Result:
=> #<OpenStruct items=[#<OpenStruct health=[#<OpenStruct goal=[#<OpenStruct activity=["1A", "2A"], id="GA">], activity=["1B", "2B"], id="GB">], goal=[#<OpenStruct activity=["1C", "2C"], id="3c">], createdAt="2018-01-01", updatedAt="2018-01-01", id="DA">]>

How to chaange my as_json method?

Now i have used the as_json method like this in my model
def as_json(options = {})
{
id: id,
diary_id: diary_id,
title: title,
post_date_gmt: date,
post_content: strip_tags(content),
smiley_id: smiley_id,
author_id: user_id,
author_name: user.display_name,
attachments: filter_attachments(options[:version]),
root_comments: format_comments(nested_comments.arrange(:order => :created_at)),
post_readings: post_readings.size,
is_read: read_by(options[:current_user])
}
end
I need to change this structure a bit as follows, Actually i want group this array by the date.
{
date_01: {
[post1], [post2], [post3]
},
date_02: {
[post1], [post2], [post3]
}
}
What should I do ?
I fixed the issue as follows
post_dates = (no_of_days.days.ago.to_date..(date_as_string.to_date + no_of_days.days)).map{ |date| date.strftime("%Y-%m-%d") }
# Arrange posts details under each date
i = 0
post_dates.each do |post_date|
posts_grouped_by_date[i] = {:post_date => post_date, :posts_for_date => diary.posts_for_date(Date.parse(post_date) )}
i = i + 1
end
render json: posts_grouped_by_date.sort_by {|hash| hash['post_date']}.as_json(current_user: current_user)
replace the values of data keys to an array of arrays. like below.
{
date_01: [
[post1], [post2], [post3]
],
date_02: [
[post1], [post2], [post3]
]
}

RUBY - Correct way of doing array of hashes inside of hash

i need to do an array of hashes inside of a hash, something like this:
merit_hash => {
students => [
{
"id": id,
"name": name,
subjects => [
{
"id": id,
"grade": grade
},
{
"id": id,
"grade": grade
}
]
},
{
"id": id,
"name": name,
subjects => [
{
"id": id,
"grade": grade
},
{
"id": id,
"grade": grade
}
]
}
]
}
Right now, i just have the array of student hashes, but i dont exactly know how to put the subject array inside of it, im doing this:
merit = {}
merit["students"] = []
students.each do |students|
student_subjects = Array.new
merit["students"].push(
{
"id" => students.id,
"name" => students.name.to_s
selected_batch_subjects.each do |subjects|
grade = FinalGrades.where(batch_subject_id:subjects.id, period_id: period.id, student_id: student.id).first.value
student_subjects.push(
{
"id" => subjects.id,
"grade"=> grade
}
)
end
}
)
end
but throws this error
unexpected '}', expecting keyword_end
when i try to close the student hash... what can i do to make this work? or, whats the best way of implementing this?
Thanks!
Something like this should work:
merit = {}
merit["students"] = []
students.each do |student|
student_information = {"id" => student.id, "name" => student.name.to_s}
student_subjects = []
selected_batch_subjects.each do |subjects|
grade = FinalGrades.where(batch_subject_id:subjects.id, period_id: period.id, student_id: student.id).first.value
student_subjects.push({"id" => subjects.id, "grade" => grade})
end
student_information[:subjects] = student_subjects
merit["students"].push(student_information)
end
The important part is adding each student's subjects to the already existing hash.
Your iterations are not very clear to me but for current loop and array push you could do like this:
merit = {}
merit["students"] = []
students.each do |students|
student_subjects = []
merit["students"] << {
"id" => students.id,
"name" => students.name.to_s
}
selected_batch_subjects.each do |subjects|
grade = FinalGrades.where(batch_subject_id:subjects.id, period_id: period.id, student_id: student.id).first.value
student_subjects << {"id" => subjects.id,"grade"=> grade}
end
end

Rails JSON : Empty/Blank data will not save in database

I have this in my controller
#geo.message_notification = {
trigger_event: params[:ng_geo][:trigger_event],
ptc: {
id: params[:ng_geo][:ptc]
},
message: params[:ng_geo][:message],
mode: params[:ng_geo][:mode],
remind_interval: params[:ng_geo][:remind_interval],
document: {
id: params[:ng_geo][:document]
}
}.to_json
will produce JSON:
{
"trigger_event":"IOR",
"ptc":
{
"id":"184"
},
"message":"could you please be faster?",
"mode":"",
"remind_interval":"",
"document":
{
"id":"234"
}
}
As you can see in the JSON, empty data (mode & remind_interval) still save in db. how to avoid this so will produce just only attr with data:
{
"trigger_event":"IOR",
"ptc":
{
"id":"184"
},
"message":"could you please be faster?",
"document":
{
"id":"234"
}
}
Well, it seems to me there are some fundamental issues here.
First of all, a controller is not a good place to do this kind of stuff.
Second, you should pass this logic to database wrapper, like active record or mongoid.
Or you could do it quick and dirty:
#geo.message_notification = {
...
}.select{|_,v| !v.blank?}.to_json
Try this solution please:
ng_geo_params = params[:ng_geo].slice(:trigger_event, :ptc, :message, :mode, :remind_interval, :document).delete_if { |k, v| v.blank? }
# OR if you can
ng_geo_params = params.require(:ng_geo).permit(:trigger_event, :ptc, :message, :mode, :remind_interval, :document).delete_if { |k, v| v.blank? }
message_notification_hash = {}
ng_geo_params.each do |k, v|
if k == :ptc || k == :document
message_notification_hash[k] = { id: v }
else
message_notification_hash[k] = v
end
end
#geo.message_notification = message_notification_hash.to_json

Rails 3 : Generate view using rabl

In my Rails Application i have Two instance variables #departments and #register
#departments =
{
"users": [
{
"departmentid": "DP11"
},
{
"departmentid": "DP12"
},
{
"departmentid": "DP13"
},
{
"departmentid": "DP10"
}
]
}
#register =
{
"attendance": [
0,
0,
2,
1
]
}
#register contains an array .
Is it possible to show like below format using rabl (attendancebydepartment.json.rabl) view
{
"users": [
{
"departmentid": "DP11",
"attendance"=0
},
{
"departmentid": "DP12",
"attendance"=0
},
{
"departmentid": "DP13",
"attendance"=2
},
{
"departmentid": "DP10",
"attendance"=1
}
]
}
My controller looks like
def attendancebydepartment
#register = Array.new
#departments = User.select('departmentid').uniq
startdate = params[:startdate]
enddate = params[:enddate]
#count = #departments.count
#departments.each do |d|
#register << (Register.where(:date => startdate..enddate , :departmentid => d.departmentid).sum(:one))+(Register.where(:date => startdate..enddate , :departmentid => d.departmentid).sum(:two))
end
end
Is it possible to add each department and its corresponding attendance to array,so that i can display like expected format.
Perhaps use the zip method to create a new variable and then present that.
irb(main):001:0> departments = {'users' => [{'id' => 1}, {'id' => 2}]}
=> {"users"=>[{"id"=>1}, {"id"=>2}]}
irb(main):002:0> register = {'attendance' => [0,1]}
=> {"attendance"=>[0, 1]}
irb(main):004:0> departments['users'].zip(register['attendance'])
=> [[{"id"=>1}, 0], [{"id"=>2}, 1]]
On the other hand, it looks like a simpler design would be to have a Department model that has a has_many association with Users. Then you could refer to the count of users directly from an instance of Department.
It may be easiest to create objects in your controller using OpenStruct, something like this, but I would recommend re-writting attendancebydepartment to not loop twice.
#users = []
#departments.each_with_index do |dep, index|
user = OpenStruct.new
user.departmentid = dep.departmentid
user.attendence = #register[index].attendence
#users << user
end
And in the view:
collection #users => :users
attribute :departmentid, :attendence

Resources