如何在每个组中分组并嵌套结果?

时间:2013-09-25 20:54:42

标签: ruby-on-rails ruby activerecord

我试着这样做:

Things.order("name").group("category_name")

我期待结果是这样的:

[
    {
    "category_name": "Cat1",
    "things":
    [
        { "name": "Cat1_Thing1" },
        { "name": "Cat1_Thing1" }
    ]
},
{
    "category_name": "Cat2",
    "things":
    [
        { "name": "Cat2_Thing3" },
        { "name": "Cat2_Thing4" }
    ]
}
]

所以我希望得到一个“类别”数组,每个类别都包含一个属于该类别的“项目”。相反,它似乎给了我一个列表,按我分组的字段排序。

注意: category_namething表中的一列。

3 个答案:

答案 0 :(得分:1)

一种选择是在Rails中进行分组(它返回一个哈希值)

Things.order("name").group_by(&:category_name)
#=> {"cat1" => [thing1,thing2,..], "cat2" => [thing3,thing4,..],..}

答案 1 :(得分:1)

尝试类似

的内容
my_grouping = Category.includes(:things).
    select("*").
    group('categories.id, things.id').
    order('name')

=> #<ActiveRecord::Relation [#<Category id: 1, name: "Oranges">, #<Category id: 2, name: "Apples">]>

但是,您仍然需要通过Thing访问my_grouping.things个对象,它们已经在您手中,您无需等待结果。这可能是您正在寻找的那种互动,而不是将它们映射到实际的Array

答案 2 :(得分:1)

ActiveRecord :: Base#group执行SQL GROUP BY。我想,但我不确定(取决于您的数据库适配器),因为您没有指定任何SELECT子句,您将获得每个类别的第一条记录。

为了实现你想要的,有不同的方式。

例如,使用#includes

Category.includes(:things).map do |category|
  {
    category_name: category.name,
    things:        things.sort_by(&:name).map{|t| {name: t.name} }
  }
end.to_json 

请注意,将模型序列化为json的标准(尽管经常不赞成)是使用(并在需要时覆盖)as_json and to_json。所以你会有类似的东西:

class Category < ActiveRecord::Base

  def as_json( options = {} )
    defaults = { only: :name, root: false, include: {links: {only: :name}} }
    super( defaults.merge(options) )
  end

end 

像这样使用:

Category.includes(:links).map(&:to_json) 

修改

由于category_name只是一个列,您可以这样做:

Thing.order( :category_name, :name ).sort_by( &:category_name ).map do |category, things|
  { category_name: category, things: things.map{|t| {name: t.name} } }
end.to_json

这样的事情可能属于模型:

def self.sorted_by_category
  order( :category_name, :name ).sort_by( &:category_name ).map do |category, things|
    { category_name: category, things: things.map{|t| {name: t.name} } }
  end 
end

所以你可以这样做:

Thing.sorted_by_category.to_json
通过这种方式,你甚至可以进一步扩大范围:

Thing.where( foo: :bar ).sorted_by_category.to_json