'Splitting' ActiveRecord collection
Let's say I have two models Post and Category:
class Post < ActiveRecord::Base
belongs_to :category
end
class Category < ActiveRecord::Base
has_many :posts
end
Is there a method that will allow me to do something like
posts = Post.find(:all)
p = Array.new
p[1] = posts.with_category_id(1)
p[2] = posts.with_category_id(2)
p[3] = posts.with_category_id(3)
...
or
p = posts.split_by_category_ids(1,开发者_如何学Go2,3)
=> [posts_with_category_id_1,
posts_with_category_id_2,
posts_with_category_id_3]
In other words, 'split' the collection of all posts into arrays by selected category ids
Try the group_by
function on Array
class:
posts.group_by(&:category_id)
Refer to the API documentation for more details.
Caveat:
Grouping should not performed in the Ruby code when the potential dataset can be big. I use the group_by
function when the max possible dataset size is < 1000. In your case, you might have 1000s of Post
s. Processing such an array will put strain on your resources. Rely on the database to perform the grouping/sorting/aggregation etc.
Here is one way to do it(similar solution is suggested by nas
)
# returns the categories with at least one post
# the posts associated with the category are pre-fetched
Category.all(:include => :posts,
:conditions => "posts.id IS NOT NULL").each do |cat|
cat.posts
end
Something like this might work (instance method of Post, untested):
def split_by_categories(*ids)
self.inject([]) do |arr, p|
arr[p.category_id] ||= []
arr[p.category_id] << p if ids.include?(p.category_id)
arr
end.compact
end
Instead of getting all posts and then doing some operation on them to categorize them which is a bit performance intensive exercise I would rather prefer to use eager loading like so
categories = Category.all(:include => :posts)
This will generate one sql query to fetch all your posts and category objects. Then you can easily iterate over them:
p = Array.new
categories.each do |category|
p[1] = category.posts
# do anything with p[1] array of posts for the category
end
Sure but given your model relationships you I think you need to look at it the other way around.
p = []
1.upto(some_limit) do |n|
posts = Category.posts.find_by_id(n)
p.push posts if posts
end
精彩评论