按父对象分组注释,按最旧注释排序父对象

时间:2010-06-04 04:11:55

标签: ruby-on-rails ruby arrays

我有包含评论的对象。作为定期电子邮件摘要的一部分,我想确定一段时间的评论,并按照最早的评论对象的顺序显示对象。

数据:

object_id  comment_id
30         40
40         42
32         41
30         43
32         44

输出:

对象#30

  • 评论40
  • 评论43

对象#40

  • 评论42

对象#32

  • 评论41
  • 评论44

我正在使用此代码将数据传输到中间数组 - 我尝试使用.group_by(&:commentable_id)一次性获取所有数据但数据没有以正确的顺序出现。

comments = account.comments.all( 
  :conditions => ["comments.created_at > ?", 8.hours.ago],
  :order => "comments.created_at asc" ).map { |c| [c.commentable_id,c.id] }

=>  [ [30,40], [40,42], [32,41], [30,43], [32,44] ]

如果我可以将数据转换为以下形式,我可以迭代数组来构建电子邮件内容......

[ [30,[40,43]], [40,[42]], [32,[41,44]] ]

但是我想知道我是否比我需要的更难......任何建议?

(我正在使用Rails 2.3和Ruby ree-1.8.7)

3 个答案:

答案 0 :(得分:1)

在单个块/元素中包含单个对象的所有注释肯定会使对它们执行任何操作时的生活更轻松。但是,我不会把它们变成一个数组数组,因为它已经是一个数组数组。我更喜欢像这样创建一个哈希:

comments_array = [ [30,40], [32,41], [40,42], [30,43], [32,44] ]
obj_with_comments = {}
comments_array.each do |x|
  obj_with_comments[x.first] ||= []
  obj_with_comments[x.first] << x.last
end

obj_with_comments #=> {40=>[42], 30=>[40, 43], 32=>[41, 44]}

但是这提出了另一个问题,即散列没有排序,所以如果你只是迭代哈希,你就会以一种随机的方式丢失你的排序。但是,您可以创建一个对象数组,然后迭代遍历哈希,如下所示:

objects = comments_array.collect{|x| x.first}.uniq
objects #=> [30, 32, 40]

# now get the hash value for each object key in order
objects.each { |obj| puts obj_with_comments[obj].inspect }

希望这是有道理的。

答案 1 :(得分:1)

您可以使用具有数组聚合的组来访问您正在查找的数组表单。

数组聚合大量依赖于数据库。 MySQL是GROUP_CONCAT。 Postgres'是ARRAY_AGG。 Sqlite没有开箱即用,但我知道你可以定义自定义聚合函数,所以这不是不可能的。

实际上没有尝试过运行此代码,但这里的内容应该指向正确的方向:

result = Object.all(
  :select => 'objects.id, GROUP_CONCAT(comment_id) AS comment_array',
  :group => 'comments.id'
).map { |c| [c.id, c.comment_array] }

我使用了第一个示例中的命名,因此您需要将'object'更改为您调用的表。希望它有意义。 Rails可能没有内置的解析数组的支持,所以它可能会为comment_array返回一个字符串,你可能需要解析它。

答案 2 :(得分:0)

试试这个:

comments = account.comments.all(
  :conditions => ["comments.created_at > ?", 8.hours.ago],
  :order => "comments.commentable_id ASC, comments.id ASC" 
).map { |c| [c.commentable_id,c.id] }

这将返回以下结果集:

=>  [ [30,40], [30,43], [32,41], [32,44], [40,42] ]

在上面的查询中,我使用id进行排序而不是create_at。如果您正在使用MySQL并且如果id是自动生成的,则此逻辑将起作用,因为新对象的id将高于旧对象的id。如果您不允许编辑注释,则此逻辑将起作用。

如果要按日期显式排序,请使用以下语法:

comments = account.comments.all(
  :joins => "accounts AS accounts ON comments.commentable_type = 'Account' AND
                                     comments.commentable_id  = accounts.id", 
  :conditions => ["comments.created_at > ?", 8.hours.ago],
  :order => "accounts.id ASC, comments.id ASC" 
).map { |c| [c.commentable_id,c.id] }