I have an array of hash, which stores data from api. How can I perform inner join on it with a table.
> posts
[{ uuid: 'post1', user_uuid: 'user1' },
{ uuid: 'post2', user_uuid: 'user2' }]
> User.all
#<ActiveRecord::Relation [#<User id: 1, uuid: "user1", name: "User1">]>
I expect to get an array like
[{ uuid: 'post1', user_uuid: 'user1', user_name: 'User1' }]
user2 does not exist in table, so it will not be in the result array.
this is sample for you code above not inner join since the other table (posts) is not relational table in database
arr1 = Array.new
users = User.all
users.each do |user|
post = posts.detect { |d| d[:user_uuid] == user.uuid }
# detect will check if here is matching between user and posts content
arr1 << {uuid: post[:uuid], user_uuid: post[:user_uuid], user_name: user.name} if post.present?
end
puts arr1
Related
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)"
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;
sql-fiddle
Thank you max for the comment.
def user_points
result = User.join(:reports)
.select(
:first_name,
:last_name,
Report.arel_table[:points].sum.as(:points),
:time_zone
)
.group(:id)
render json: { data: result }
end
Output:
data:
first_name: "Test1"
last_name: "Test1"
points: 100
first_name: "Test2"
last_name: "Test2"
points: 200
first_name: "Test3"
last_name: "Test3"
points: 300
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
I'm trying to create this JSON layout using jbuilder:
"entities" : {
"users": {
1: {name: 'abs', age: 44},
2: {name: 'arms', age: 12},
3: {name: 'legs', age: 34},
}
}
I have this so far:
json.entities do
json.users #response.users do |user|
json.(user, :id)
end
end
But this is returning:
entities: {
users: [
{
id: 1
}
]
}
I need to make the key in "users" to be the user.id value, and then list the attributes.
This will get you the hash you need. You'll have to call .to_json on it if you need the string.
hash = {users:{}}
#response.users.pluck(:id, :name, :age).each {|u| hash[:users][u[0]] = {name: u[1], age: u[2]}]}
# hash.to_json
Title may sound off but below explains more.
I need to know if an item is included in an array object:
[
{ id: 12345, name: "Bob", email: "bob#builder.com" },
...
{ id: 13456, name: "job", email: "joe#farm.com" }
]
In english: If this email present, give me their id
users = User.all
users.any?{|u| u.email == "bob#builder.com"} # true
That will be true. Now, how to get the id of the user which is "12345"? Note, I will not know the id.
Since you seem to be using ActiveRecord, it would be faster to query the database for the email:
# returns the user id or nil, if not found
User.find_by(email: 'bob#builder.com').pluck(:id)
You can try like this if you have array of hashes:
arr.map do |h|
h[:id] if h[:email] == "bob#builder.com"
end.compact
It will return the value of id in an array of all the hashes which satisfies the condition.
Here is the scenario, I have these objects. Let's assume that this is a User:
The object came from:
#user = User.all
User Object
[<#User id: 1, firstname: "John", lastname: "Pond">,<#User id: 2, firstname: "Paul", lastname: "Rich">,<#User id: 3, firstname: "Jasmine", lastname: "Ong">]
How can I move one object up, for example I want to move User.id == 2? The result I want is shown below.
[<#User id: 2, firstname: "Paul", lastname: "Rich">,<#User id: 1, firstname: "John", lastname: "Pond">,<#User id: 3, firstname: "Jasmine", lastname: "Ong">]
I already got the answer. Here is what I made to made my question above worked.
#users = User.all
user_ids = User.pluck(:id)
user_ids.delete(2)
new_user_ids = [2]
user_ids.each do |id|
new_user_ids << id
end
#users.sort_by { |user| new_user_ids.index(user.id) }
And this made perfect!
We can also do it in a way like this:
Add a new method to Array. lib/rails_extensions.rb
class Array
def swap!(a, b = a - 1)
self[a], self[b] = self[b], self[a]
self
end
end
Then add this in config/environment.rb
require 'rails_extensions'
So we can use the method swap! for arrays and it will swap the object with the one before it. We can do something like this:
#users = User.all #[<#User id: 1>, <#User id: 2>]
user_id = #users.rindex {|user| user.id == 2}
#users = #users.swap!(user_id) #[<#User id: 2>, <#User id: 1>]
is this too ugly?
hash = [{ id: 1}, {id: 2}, {id: 3}]
hash.unshift(hash.delete(hash.select {|h| h[:id] == 2 }.first))
=> [{:id=>2}, {:id=>1}, {:id=>3}]
I have a PORO TutorProfileHandler that has a function json that returns a hash.
class TutorProfileHandler
def initialize(opts)
#profile = opts[:tutor_profile]
end
def json
tutor = #profile.tutor
return {
id: tutor.id,
first_name: tutor.first_name,
last_name: tutor.last_name.first + '.',
school: #profile.school,
avatar: #profile.avatar.url,
bio: #profile.bio,
academic_level: #profile.academic_level,
headline: #profile.headline,
major: #profile.major,
rate: #profile.rate,
rating: #profile.rating,
courses: JSON.parse(#profile.courses),
video_url: #profile.video_url
}
end
end
In my index_tutor_profiles.json.jbuilder, I would like to generate
{
tutor_profile: [{id: 1, ...}, {id: 2, ...}, ...],
tutor_sum: 20
}
However when I do this
json.tutor_profiles (#tutor_profiles) do |profile|
TutorProfileHandler.new({tutor_profile: profile}).json
end
json.tutor_sum #tutor_sum
It gives me an empty array for tutor_profiles.
However if I move everything from TutorProfileHandler.json to the jbuilder file, it works. How do I explicitly include the hash returned by TutorProfileHandler.json in the jbuilder array?
Note: This returns an array, but it creates a new key-value pair array:
json.tutor_profiles json.array(#tutor_profiles) do |profile|
TutorProfileHandler.new({tutor_profile: profile}).json
end
Result:
{
array: [{id: 1, ...}, {id: 2, ...}, ...],
tutor_profile: [],
tutor_sum: 20
}
There is a ugly approach:
json.tutor_profiles #tutor_profiles do |profile|
tmp_json = TutorProfileHandler.new({tutor_profile: profile}).json
json.(tmp_json, *(tmp_json.keys))
end
I think the best practise is directly nesting inside model. You can get more information from the its github page.