遍历记录集合并将对象传回前端的最佳方法是什么?

What is the best way to loop through a collection of records and pass back an object to the front end?

我有一个 returns 用户报告的控制器,其中一种方法总结了每个用户所述报告的要点。我想把这个数据的一个对象传回给前端,这样就可以显示了。理想情况下,我的对象的形状应该是这样的:

data: {
  users: {
    $user_id: {
      name: "Foo Bar",
      points: 100
    },
    $user_id: {
      name: "Foo Bar Two",
      points: 10
    }
  }
}

然而我目前的实现并不是像这样构建对象,而是简单地添加到一个大对象中。

我的代码如下所示:

  def user_points
    hash = {}
    User.all.each do |u|
      user_points = Report.select("points").where("user_id = ?", u.id).sum("points")
      hash.merge!(
        user: 
        {
          first_name: u.first_name,
          last_name:u.last_name,
          time_zone: u.time_zone
        }
      )
    end
    render json: { data: hash }
  end

并且生成的对象只包含一个大对象中的最后一个用户

data:
  user:
  first_name: "Test"
  last_name: "Test"
  points: 200
  time_zone: "Pacific Time (US & Canada)"

如 dbugger 所述,您需要为每个哈希条目提供唯一键,否则合并将仅替换现有值。

例如:

{a: :foo}.merge(b: :bar)
=> {:a=>:foo, :b=>:bar} 

{a: :foo}.merge(b: :bar).merge(a: :foo_bar)
{:a=>:foo_bar, :b=>:bar}

您可能要考虑返回 json 数组而不是具有唯一 属性 名称的对象。

也许是这样的?

  def user_points
    result = User.all.map do |u|
      points = Report.select("points").where("user_id = ?", u.id).sum("points")
        {
          first_name: u.first_name,
          last_name:u.last_name,
          time_zone: u.time_zone
          points: points
        }
    end
    render json: { data: result }
  end

您也可以通过连接 table 然后对连接的 table 执行聚合来获得相同的结果。

select users.id, users.name, sum(reports.points) as points from users join reports on users.id = reports.user_id group by users.id;

sql-fiddle

谢谢 max 的评论。

def user_points
  result = User.join(:reports)
            .select(
              :first_name,
              :last_name,
              Report.arel_table[:points].sum.as(:points),
              :time_zone
            )
            .group(:id)
  render json: { data: result }
end

输出:

data:
  first_name: "Test1"
  last_name: "Test1"
  points: 100

  first_name: "Test2"
  last_name: "Test2"
  points: 200

  first_name: "Test3"
  last_name: "Test3"
  points: 300