开发者

'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 Posts. 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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜