SwiftUI入门 - Core Data数据的分组、聚合和汇总

置顶

菜鸟入门笔记,如有谬误之处还请大佬指出

深耕细作 笃行致远

相关文章

如需获取其他SwiftUI知识点,请移步至SwiftUI入门专栏

前言

在上一章笔记《SwiftUI入门 - Core Data包装器@FetchRequest的排序、筛选和分页》中,我们知晓了数据的排序、查询和分页等相关知识点。在本章节中我们将学习Core Data数据分组、聚合和汇总的使用,也是对上一章节内容的补充。

物料

直接沿用上一章的实体和测试数据,如若还不清楚实体的创建与测试数据的导入,请移步至上一章,这里不详细阐述此过程。

创建测试实体

image.png

创建测试数据

    // 定义测试序列化数据的结构体
    struct UserItem{
        var name:String;
        var score:Int32;
        var timestamp: Date;
    }

    // 初始化测试数据方法
    func initTestData(){
        let list: [UserItem] = [
            UserItem(name: "张三", score: 60, timestamp: Date()),
            UserItem(name: "李四", score: 70, timestamp: Date().addingTimeInterval(60)),
            UserItem(name: "王五", score: 10, timestamp: Date().addingTimeInterval(3600)),
            UserItem(name: "赵六", score: 20, timestamp: Date().addingTimeInterval(120)),
            UserItem(name: "刘七", score: 70, timestamp: Date().addingTimeInterval(3600)),
            UserItem(name: "周一", score: 40, timestamp: Date().addingTimeInterval(3600)),
            UserItem(name: "将二", score: 50, timestamp: Date().addingTimeInterval(5960)),
            UserItem(name: "王五", score: 100, timestamp: Date().addingTimeInterval(1230)),
            UserItem(name: "张三", score: 70, timestamp: Date().addingTimeInterval(567)),
            UserItem(name: "建安", score: 90, timestamp: Date().addingTimeInterval(345)),
        ];

    	// 遍历存入缓存
        for element in list{
            let user = User(context: viewContext)
            user.name = element.name
            user.score = element.score
            user.timestamp = element.timestamp
        }
        do {
    		// 保存数据入库
            try viewContext.save()
        } catch {
            let nsError = error as NSError
            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
        }
    }

分组

分组的作用不言而喻,在诸多的业务场景中,例如中台、大屏和数据分析等场景均在使用,是对某个特定事物的分组,以达到归类的目的

    import SwiftUI
    import CoreData

    struct GroupView: View {
        // 获取 BestBeforeApp 传入的环境变量并赋值给 viewContext
        @Environment(\.managedObjectContext) var viewContext
        // 分组包装器
        @SectionedFetchRequest<Int32, User>(
            sectionIdentifier: \.score,
            sortDescriptors: [
                NSSortDescriptor(keyPath: \User.score, ascending: false)
            ]
        )
        var users: SectionedFetchResults<Int32, User>

        var body: some View{
            // 滚动视图
            ScrollView{
                VStack{
                    // 循环分组用户列表
                    ForEach(users){section in
                        HStack{
                            // 获取当前分组值
                            Text("\(section.id)").padding(8)
                            Spacer()
                        }
                        .frame(maxWidth: .infinity)
                        .foregroundColor(Color.white)
                        .background(Color.gray)
                        
                        // 循环当前分组用户列表
                        ForEach(section) { item in
                            HStack{
                                Text(item.name ?? "")
                                Spacer()
                                Text(formatDate(date: item.timestamp!))
                            }
                        }
                    }
                }.padding()
            }
        }
        
        // 日期格式化函数
        func formatDate(date: Date) -> String {
            let formatter = DateFormatter()
            formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
            return formatter.string(from: date)
        }
    }

代码执行逻辑:

  1. 我们使用 @SectionedFetchRequest<Int32, User> 来获取分组数据,其泛型 Int32 表示约束 sectionIdentifier 的数据类型为 Int32
  2. 形参 sectionIdentifier 接受一个实体属性,且这个属性必须与上述 @SectionedFetchRequest<Int32, User> 中左侧约束类型一致,代码中传入了 \.score 表示用实体属性 score 的值作为分组
  3. 然后我们通过 ForEach 的嵌套遍历展示分组数据,循环体中的 section.id 为实体属性值

输出结果如下:

image.png

当然我们也可以使用用户名来分组,也就是用 name 属性的值

    // 分组包装器
    @SectionedFetchRequest<String, User>(
        sectionIdentifier: \.name,
        sortDescriptors: [
            NSSortDescriptor(keyPath: \User.score, ascending: false)
        ]
    )
    var users: SectionedFetchResults<String, User>

注意保持泛型的数据类型与分组属性的数据类型一致,这里我们将数据类型替换成 String

聚合与汇总

直接贴代码

    import SwiftUI
    import CoreData

    struct MaxValueView: View {
        // 获取 BestBeforeApp 传入的环境变量并赋值给 viewContext
        @Environment(\.managedObjectContext) var viewContext

        var body: some View{
            Text("score最大值为:\(String(fetchMaxValue()!))")
        }
        
        // 获取最大值
        func fetchMaxValue() -> Int32? {
            // 创建一个FetchRequest,结果类型为字典
            let fetchRequest = NSFetchRequest<NSDictionary>(entityName: "User")
            fetchRequest.resultType = .dictionaryResultType

            // 创建一个NSExpression,用于表示"value"属性
            let expression = NSExpression(forKeyPath: "score")

            // 创建一个NSExpression,用于计算平均值
            let averageExpression = NSExpression(forFunction: "max:", arguments: [expression])

            // 创建一个NSExpressionDescription,用于描述计算结果的属性
            let expressionDescription = NSExpressionDescription()
            expressionDescription.name = "maxValue" // 属性的名称
            expressionDescription.expression = averageExpression // 属性的表达式
            expressionDescription.expressionResultType = .integer32AttributeType // 属性的数据类型

            // 将NSExpressionDescription添加到FetchRequest的propertiesToFetch数组中
            fetchRequest.propertiesToFetch = [expressionDescription]

            do {
                // 执行FetchRequest,获取结果
                let result = try viewContext.fetch(fetchRequest)

                // 从结果中提取计算的平均值,并返回
                if let averageValue = result.first?["maxValue"] as? Int32 {
                    return averageValue
                }
            } catch {
                // 处理错误
                print("Error fetching average value: \(error)")
            }

            // 如果发生错误或没有找到结果,则返回nil
            return nil
        }
    }

代码执行逻辑:

  1. 我们通过 NSFetchRequest 创建了查询请求
  2. 使用 NSExpressionforKeyPath 指定聚合属性名
  3. 使用 NSExpressionforFunction 指定聚合函数,这里也可以替换为 min sum average等函数。
  4. 创建 expressionDescription 结构体,添加到 fetchRequest.propertiesToFetch 数组中
  5. 然后通过 viewContext.fetch 获取到 score 的最大值
  6. 最后在 body 中使用 Text 展示

image.png

总结

  1. 我们了解到Core Data提供了 @SectionedFetchRequest 包装器中的 sectionIdentifier 来指定分组属性实现数据分组
  2. 使用了两个 ForEach 嵌套循环去渲染分组后的结果,其中循环体中的 section.id 为实体属性值
  3. 使用 NSExpressionNSExpressionDescription 来指定聚合函数,并添加到 fetchRequest.propertiesToFetch 数组中获取聚合结果

别错过精彩内容,快来关注我们的微信公众号【寻桃】!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寻桃

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值