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
相关产品推荐
相关产品推荐

