You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails中循环对象数组时插入对应expense_type_id的小计对象

问题

在Rails项目中,Controller的index方法代码如下:

def index
  @expenses = Expense.all.order(:expense_type_id)
end

@expenses的结构示例:

[#<Expense:0x00007faeb044c6e8
  id: 1,
  amount: 120,
  expense_type_id: 1>,
 #<Expense:0x00007faeb044c2b0
  id: 2,
  amount: 1200,
  expense_type_id: 1>,
 #<Expense:0x00007faeb044c2b0
  id: 3,
  amount: 11200,
  expense_type_id: 3>,
 #<Expense:0x00007faeb044c2b0
  id: 4,
  amount: 1120,
  expense_type_id: 4>,
 #<Expense:0x00007faeb044c2b0
  id: 5,
  amount: 1120,
  expense_type_id: 4>]

当前使用index.json.jbuilder输出JSON:

json.records do
  json.array! @expenses do |expense|
    json.amount expense.billed
    json.id expense.id
    json.expense_type_id expense.expense_type_id
  end
end

当前输出结果:

records = [{amount: 120, id: 1, expense_type_id: 1}, {amount: 1200, id: 2, expense_type_id: 1}, {amount: 11200, id: 3, expense_type_id: 3}, {amount: 1120, id: 4, expense_type_id: 4}, {amount: 1120, id: 5, expense_type_id: 4}]

期望输出:在每个expense_type_id对应的所有对象后插入小计对象,包含该分组的金额总和,id为'Subtotal',expense_type_id为nil:

records = [{amount: 120, id: 1, expense_type_id: 1}, {amount: 1200, id: 2, expense_type_id: 1}, {amount: 1320, id: 'Subtotal', expense_type_id: nil}, {amount: 11200, id: 3, expense_type_id: 3}, {amount: 11200, id: 'Subtotal', expense_type_id: nil}, {amount: 1120, id: 4, expense_type_id: 4}, {amount: 1120, id: 5, expense_type_id: 4}, {amount: 2240, id: 'Subtotal', expense_type_id: nil}]

需要实现:循环对象数组时,为每个expense_type_id分组插入对应的小计对象。

解决方案

方式一:在Controller中预处理数据(推荐)

先对@expenses按expense_type_id分组,计算每组金额总和后,将原始记录与小计项合并为新数组,Jbuilder只需直接渲染即可。

修改Controller的index方法:

def index
  # 按expense_type_id分组,并计算每组总金额
  grouped_expenses = Expense.all.order(:expense_type_id).group_by(&:expense_type_id)
  
  @records = grouped_expenses.flat_map do |type_id, expenses|
    # 保留原始支出记录,追加小计项
    total_amount = expenses.sum(&:billed) # 与Jbuilder中使用的字段保持一致
    expenses + [{ id: 'Subtotal', amount: total_amount, expense_type_id: nil }]
  end
end

修改index.json.jbuilder:

json.records do
  json.array! @records do |item|
    json.amount item[:amount] || item.amount # 兼容ActiveRecord对象和hash
    json.id item[:id] || item.id
    json.expense_type_id item[:expense_type_id] || item.expense_type_id
  end
end

方式二:直接在Jbuilder中处理

若不想修改Controller,可直接在Jbuilder内完成分组与小计插入:

json.records do
  grouped_expenses = @expenses.group_by(&:expense_type_id)
  
  grouped_expenses.each do |type_id, expenses|
    # 渲染当前分组的所有支出记录
    json.array! expenses do |expense|
      json.amount expense.billed
      json.id expense.id
      json.expense_type_id expense.expense_type_id
    end
    
    # 渲染小计项
    json.set! do
      json.amount expenses.sum(&:billed)
      json.id 'Subtotal'
      json.expense_type_id nil
    end
  end
end

注意事项

  • 确保金额字段一致:如果Jbuilder中使用expense.billed,计算总和时也要用sum(&:billed),避免字段不匹配。
  • 方式一的优势是将数据处理逻辑集中在Controller,Jbuilder专注于渲染,代码职责更清晰,也便于后续复用处理后的@records数据。

内容的提问来源于stack exchange,提问作者user12763413

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 20:12:56