【问题标题】:grouping comments by parent object, ordering parent object by oldest comment按父对象分组注释,按最旧注释对父对象排序
【发布时间】:2010-06-04 04:11:55
【问题描述】:

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

数据:

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)

【问题讨论】:

    标签: ruby-on-rails ruby arrays


    【解决方案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] }
    

    我使用了第一个示例中的命名,因此您需要将“对象”更改为您的表的名称。希望这是有道理的。 Rails 可能没有对解析数组的内置支持,因此它可能会为 comment_array 返回一个字符串,您可能必须对其进行解析。

    【讨论】:

    • 感谢提示...这是我使用的代码... result = Comment.all( :select => 'commentable_id, GROUP_CONCAT(id) as comment_array', :group => 'commentable_id ' ).map { |c| [c.commentable_id, c.comment_array] }.reverse
    【解决方案2】:

    将单个对象的所有 cmets 放在单个块/元素中肯定会让在对它们进行任何操作时更轻松。但是,我不会将它们变成数组数组,因为它已经是数组数组。我更喜欢像这样创建一个哈希:

    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 }
    

    希望这是有道理的。

    【讨论】:

    • 是的,有道理 - 谢谢 - 绝对是只使用 2 个对象的可行解决方案
    【解决方案3】:

    试试这个:

    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。如果您不允许编辑 cmets,则此逻辑将起作用。

    如果您想明确按日期排序,请使用以下语法:

    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] }
    

    【讨论】:

    • 我刚刚修改了问题以将对象的输出按非升序排列...您的解决方案只是按 id 对它们进行排序,我希望它们按最旧的评论排序到最新的评论
    • 更新了答案,看看吧。
    猜你喜欢
    • 1970-01-01
    • 2019-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-29
    • 1970-01-01
    相关资源
    最近更新 更多