如何从 SwiftUI 中的 NSManagedObject 中删除重复日期(没有时间)

How to remove duplicate dates (without time) from NSManagedObject in SwiftUI

我有这个 class,它是从 Xcode/核心数据自动生成的,用于名为 Activity 的核心数据实体。

@objc(Activity)
public class Activity: NSManagedObject {

}

extension Activity {

    @nonobjc public class func fetchRequest() -> NSFetchRequest<Activity> {
        return NSFetchRequest<Activity>(entityName: "Activity")
    }

    @NSManaged public var name: String
    @NSManaged public var date: Date

}

我怎样才能得到具有不同日期(没有时间)的结果,只有一个视图没有重复日期,而另一个视图有所有日期(未过滤)?

我不确定,如果我需要修改我的 Activity Class 并使用自定义函数扩展它,或者我是否可以对 @FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \Activity.name, ascending: true)]) var activites: FetchedResults<Activity> 应用过滤器,或者如果我需要直接在 List(activities, id: \.self).

中处理请求的结果

我需要同时拥有所有日期和筛选日期的灵活性。过滤后的活动不需要名称,也不能是唯一的。我只想要一个包含日期(没有时间)字符串的列表。未过滤的列表需要名称和日期。

例如:

应该过滤到

你描述的问题并没有那么简单。

您使用的 Date 是从某个时间点以秒为单位定义的时间戳。它与任何日历无关。 但是,您显示的字符串是基于日历的,并且是对给定时间戳的解释。

因此,当使用日历(包括时区)时,两个不同的用户对单个日期的解释可能会有所不同,主要取决于时区。

我认为数据库将 Date 对象存储为 64 位浮点值。因此,要获得过滤日期,您基本上是在要求获得所有唯一值,其中这些值首先由一些荒谬的“日历”算法四舍五入。祝你好运。

最简单的方法是在内存中执行此操作。您可以获取所有条目,然后使用字典对您的条目进行分组:

func sortEntriesByDays(_ activities: [Activity]) -> [Date: [Activity]] {
    let calendar = Calendar.autoupdatingCurrent
    
    var sortedItems: [Date: [Activity]] = [Date: [Activity]]()
    
    activities.forEach { activity in
        let dayStartDate = calendar.date(from: calendar.dateComponents([.year, .month, .day], from: activity.date))!
        
        var activities: [Activity] = sortedItems[dayStartDate] ?? [Activity]()
        activities.append(activity)
        
        sortedItems[dayStartDate] = activities
    }
    
    return sortedItems
}

您只需调用 .keys 即可从此方法的结果中获取所有唯一日期。

这可能是最简单、最安全的解决方案。唯一的问题是当你的数据库中有很多条目时,你可能不想加载所有(或很多)条目而只是为了显示一些。在这些情况下,获取结果控制器非常有用,但要使用它,您可能需要更改数据库。

一种方法是在 Activity 中存储 2 个日期。一个是您已经存储的实际日期,另一个是它所属日期的开始。您可以简单地创建一个可以设置两个日期的 setDate 方法;第二个简单地使用代码:calendar.date(from: calendar.dateComponents([.year, .month, .day], from: activity.date))!.

一种非常相似的方法是只使用格式 yyyy-MM-dd 添加一个字符串,正如您所描述的那样。与之前的想法没有太大变化。

然后另一种方法是创建另一个实体 Day,它与您的 activity 对象保持多对一关系。所以每个 activity 都应该准确地引用一天。因此,当一个 activity 应该被插入到数据库中时,它应该尝试找到一个对应的日期对象并与之连接。如果没有这样的 Day 对象,则需要创建一个。修改对象时,需要重新检查它是否属于同一天。当 activity 从 Day 对象中删除时,您还需要检查是否还有其他活动,并在没有活动时从数据库中删除 Day 对象。

但是,无论您使用这 3 个选项中的哪个选项,当用户更改时区(可能是由于旅行)时,您都可能会遇到问题。当发生这种情况时,代码 calendar.date(from: calendar.dateComponents([.year, .month, .day], from: activity.date))! 将产生不同的结果,并且 activity 现在可能应该属于不同的 Day 对象。您可以检测到该更改并重组您的数据库...