I have a controller that returns user reports, and one of the methods sums up the points of said reports, per user. I want to pass back an object of this data to the front end so it can be displayed. Ideally my object would be shaped like this:
data: {
users: {
$user_id: {
name: "Foo Bar",
points: 100
},
$user_id: {
name: "Foo Bar Two",
points: 10
}
}
}
However my current implementation is not building the object like this, and simply adding to one big object.
My code looks like this:
def user_points
hash = {}
User.all.each do |u|
user_points = Report.select("points").where("user_id = ?", u.id).sum("points")
hash.merge!(
user:
{
first_name: u.first_name,
last_name:u.last_name,
time_zone: u.time_zone
}
)
end
render json: { data: hash }
end
and the resulting object only included the last user in one big object
data:
user:
first_name: "Test"
last_name: "Test"
points: 200
time_zone: "Pacific Time (US & Canada)"
CodePudding user response:
As mentioned by dbugger you need to provide a unique key for each hash entry otherwise merge will just replace an existing value.
For example:
{a: :foo}.merge(b: :bar)
=> {:a=>:foo, :b=>:bar}
and
{a: :foo}.merge(b: :bar).merge(a: :foo_bar)
{:a=>:foo_bar, :b=>:bar}
You might want to consider returning a json array rather than an object with unique property names.
maybe something like this?
def user_points
result = User.all.map do |u|
points = Report.select("points").where("user_id = ?", u.id).sum("points")
{
first_name: u.first_name,
last_name:u.last_name,
time_zone: u.time_zone
points: points
}
end
render json: { data: result }
end
CodePudding user response:
You can also achieve the same result by joining both the table and then performing aggregation on joined table.
select users.id, users.name, sum(reports.points) as points from users join reports on users.id = reports.user_id group by users.id;
def user_points
result = User.join(:reports)
.select("users.first_name, users.last_name, SUM(reports.points) AS points, users.time_zone")
.group("users.id")
render json: { data: result }
end
Output:
data:
first_name: "Test1"
last_name: "Test1"
points: 100
time_zone: "Pacific Time (US & Canada)"
first_name: "Test2"
last_name: "Test2"
points: 200
time_zone: "Pacific Time (Asia)"
first_name: "Test3"
last_name: "Test3"
points: 300
time_zone: "Pacific Time (Europe)"