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

SwiftUI+CoreData:解决@SectionedFetchRequest按月分组时累计金额错误问题

SwiftUI中使用@SectionedFetchRequest按月分组实现逐笔累计余额

问题

使用CoreData的@SectionedFetchRequest将交易数据按.month分组后,无法正确计算逐笔累计余额——所有交易显示的都是当月总额,需要实现逐笔累计计算,且在添加/删除交易时自动更新累计值。

现有交易数据

DateAmountIndex
June , 30, 2023+50
June , 30, 2023-101
June , 28, 2023-42
May , 17, 2023+150
May , 09, 2023-21
April, 19, 2023-40
April, 12, 2023+51

当前代码

struct TransactionsDetail: View {
    @SectionedFetchRequest<String, TransactionEntity>(
        sectionIdentifier: \.month,
        sortDescriptors: [SortDescriptor(\.transactionDate, order: .reverse)]
    ) private var transactionGroupByMonth

    var body: some View {
        List {
            ForEach(Array(transactionGroupByMonth.enumerated()), id: \.offset) { (index, monthDate) in
                Section {
                    ForEach(monthDate) { item in
                        HStack{
                            HStack {
                                Text(item.transactionDate, style: .date)
                                Spacer()
                                Text("\(String(format: "%.2f",item.transactionAmount))")  
                                Text("\(String(format: "%.2f",transactionGroupByMonth[index].reduce(0) {$0 + $1.transactionAmount}))")
                            }
                        }
                    }
                } header: {
                    Text(monthDate.id)
                }
            }
            .padding(.vertical, 1)
        }
    }
}

当前错误结果

所有交易显示当月总额,而非逐笔累计:

DateAmountAccumulated Amount
June , 30, 2023+5-9
June , 30, 2023-10-9
June , 28, 2023-4-9
May , 17, 2023+15+13
May , 09, 2023-2+13
April, 19, 2023-4+1
April, 12, 2023+5+1

期望结果

逐笔累计计算余额,增删交易时自动更新:

DateAmountAccumulated Amount
June , 30, 2023+5+5
June , 30, 2023-10-5
June , 28, 2023-4-9
May , 17, 2023+15+15
May , 09, 2023-2+13
April, 19, 2023-4-4
April, 12, 2023+5+1

注:修正了原期望结果中的计算错误,按排序后的逐笔累加逻辑呈现正确值

解决方案

核心是计算当前交易及之前所有交易的累加值,而非整月总额。通过以下步骤实现:

1. 添加前缀和计算扩展

为Collection添加扩展,快速计算前缀和数组(一次计算,多次复用,提升性能):

extension Collection where Element: AdditiveArithmetic {
    func prefixSums() -> [Element] {
        var sums = [Element]()
        var currentSum: Element = .zero
        for element in self {
            currentSum += element
            sums.append(currentSum)
        }
        return sums
    }
    
    // 针对交易实体的重载,直接提取金额计算前缀和
    func transactionPrefixSums() -> [Double] where Element == TransactionEntity {
        self.prefixSums { $0.transactionAmount }
    }
}

2. 修改视图代码

在每个月份分组内,先计算该组的前缀和数组,再遍历交易时取对应索引的累计值:

struct TransactionsDetail: View {
    @SectionedFetchRequest<String, TransactionEntity>(
        sectionIdentifier: \.month,
        sortDescriptors: [SortDescriptor(\.transactionDate, order: .reverse)]
    ) private var transactionGroupByMonth

    var body: some View {
        List {
            // 直接遍历分组,无需enumerated()
            ForEach(transactionGroupByMonth) { section in
                let transactions = Array(section)
                // 预计算当前月份的逐笔累计金额
                let accumulatedAmounts = transactions.transactionPrefixSums()
                
                Section(header: Text(section.id)) {
                    // 遍历交易时同时拿到索引和实体
                    ForEach(Array(transactions.enumerated()), id: \.element.id) { idx, item in
                        HStack {
                            Text(item.transactionDate, style: .date)
                            Spacer()
                            Text(String(format: "%.2f", item.transactionAmount))
                            // 取对应索引的累计值
                            Text(String(format: "%.2f", accumulatedAmounts[idx]))
                        }
                    }
                }
            }
            .padding(.vertical, 1)
        }
    }
}

原理说明

  • 原代码错误原因:transactionGroupByMonth[index].reduce(0) { $0 + $1.transactionAmount }是对整个月份分组的所有金额求和,因此每个交易都显示当月总额。
  • 修正逻辑:通过enumerated()拿到交易在分组内的索引,结合前缀和数组,直接取到从分组第一笔到当前交易的累加值。
  • 自动更新:由于@SectionedFetchRequest会监听CoreData数据变化,当增删交易时,分组数据自动刷新,前缀和数组也会重新计算,实现累计值自动更新。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 06:47:57