user3160455
user3160455

Reputation: 11

Fetch ActiveRecord model for each key in array of hashes

I have an array of hashes with comment id and his title:

arr = [{comment_id: 1, title: 'Title 1'}, {comment_id: 5, title: 'Title 2'}]

For each hash I have to add new :key that contain active record model as value:

arr = [{comment_id: 1, user: #<User0x00007fd88dfe1b28>..., title: 'Title 1'},{comment_id: 5, user: #<User0x00005fd58dfe1a31>..., title: 'Title 2'}]

The first solution I found is to make a query for each hash:

arr.each do |h|
 h[:user] = Comment.find(h[:comment_id]).user
end

but will be much better to do it with single sql-query something like:

comment_ids = arr.map {|h| h[:comment_id] }
users = User.joins(:comments).order('comments.id').where(comments: {id: comment_ids })

# so here I need to match User with comment_id from comment_ids 

Would be nice if someone suggest solution to do this.

Upvotes: 0

Views: 744

Answers (1)

max pleaner
max pleaner

Reputation: 26758

First of all, I would find the comments matching the comment IDs, but also includes(:user) (see Rails eager loading guide) so that we can get the user associated with a comment without needing an additional query:

comments = Comment.where(id: comment_ids).includes(:user)

From this point there are two ways you can go about things. First Enumerable#find (different than ActiveRecord::Base#find, by the way) to pick out the comment corresponding to a particular id, and then read the user from that comment record

arr.each do |h|
  comment = comments.find { |comment| comment.id == h[:comment_id] }
  h[:user] = comment.user
end

Another way is to use some enumerable manipulation to create a hash where keys are comment IDs, and values are user objects. See index_by and transform_values

users_by_comment_id = comments.index_by(&:id).transform_values(&:user)

arr.each do |h|
  h[:user] = users_by_comment_id[h[:comment_id]]
end

Upvotes: 3

Related Questions