«Разделение» коллекции ActiveRecord - PullRequest
4 голосов
/ 16 июля 2010

Допустим, у меня есть две модели Post и Category:

class Post < ActiveRecord::Base
  belongs_to :category
end

class Category < ActiveRecord::Base
  has_many :posts
end

Есть ли способ, который позволит мне сделать что-то вроде

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,2,3)

=> [posts_with_category_id_1, 
    posts_with_category_id_2,
    posts_with_category_id_3]

Другими словами, «разбить» коллекцию всех сообщений на массивы по идентификаторам выбранной категории

Ответы [ 4 ]

12 голосов
/ 16 июля 2010

Попробуйте функцию group_by в классе Array:

posts.group_by(&:category_id)

Подробнее см. В документации API .

Предупреждение:

Группировка не должна выполняться в коде Ruby, когда потенциальный набор данных может быть большим.Я использую функцию group_by, когда максимально возможный размер набора данных <1000. В вашем случае у вас может быть 1000 с <code>Post с.Обработка такого массива будет загружать ваши ресурсы.Полагайтесь на базу данных, чтобы выполнить группировку / сортировку / агрегирование и т. Д.

Вот один из способов сделать это (подобное решение предлагается 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
0 голосов
/ 16 июля 2010

Вместо того, чтобы получать все сообщения и затем выполнять некоторые операции с ними, чтобы классифицировать их, что является упражнением, требующим высокой производительности, я бы предпочел использовать нетерпеливую загрузку, например,

categories = Category.all(:include => :posts)

Это сгенерирует один SQL-запрос для извлечения всех ваших сообщений и объектов категории. Тогда вы можете легко перебрать их:

p = Array.new
categories.each do |category| 
  p[1] = category.posts
  # do anything with p[1] array of posts for the category
end
0 голосов
/ 16 июля 2010

Примерно так может работать (метод экземпляра Post, не проверен):

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
0 голосов
/ 16 июля 2010

Конечно, но учитывая ваши модельные отношения, я думаю, вам нужно взглянуть на это с другой стороны.

p = []
1.upto(some_limit) do |n|
  posts = Category.posts.find_by_id(n)
  p.push posts if posts
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...