通过属性过滤掉相似对象的谓词

Posted

技术标签:

【中文标题】通过属性过滤掉相似对象的谓词【英文标题】:a predicate to filter out similar objects by an attribute 【发布时间】:2013-06-25 20:20:48 【问题描述】:

我正在执行一个没有任何谓词的常规 NSFetchRequest 来获取 100 个托管对象(航班),每个 Flight 实体都有一个名为 NSString 类型的属性(航班代码),这个属性不是唯一的,所以 2飞行对象可能具有相同的 flightCode 。 但是,我想获取所有航班对象,通过仅从相似之处采取一个航班来过滤掉具有相同航班代码的航班,即

如果 fetch 请求返回 5 个航班,如下所示:

flight1:flightCode = ABC

flight2:flightCode = AA

flight3: flightCode = ABC

flight4:flightCode = ABC

flight5:flightCode = DEF

那么获取请求必须过滤掉具有 flightCode ABC 的 3 个航班中的任意两个,并且只采用这 3 个中的任意一个。

此过滤所需的NSPredicate 是什么?

附言航班:1、3 和 4 在其他属性中可能不同,即航班 1 的名称可能与航班 3 的名称不同。

提前致谢。

【问题讨论】:

您是在寻找唯一的航班代码,还是希望从每组相同的航班代码中随机获得一个 Flight 实例? @BarryWark 我正在从每组相同的航班代码中寻找一个随机 Flight 实例。 我不认为你可以用NSFetchRequest 完成你想要的。您正在寻找的是分组操作的一些变体。在 Core Data 中,这必须在内存中完成。看起来您使用的是NSArrayController @BarryWark 能否详细说明或显示一段代码? 【参考方案1】:

如果您只想要所有不同航班代码的列表,这可能会满足您的需求:

NSEntityDescription *entity = [NSEntityDescription entityForName:@"Flight"
    inManagedObjectContext:moc];
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entity.name];
request.resultType = NSDictionaryResultType;
request.returnsDistinctResults = YES;
request.propertiesToFetch = @[ entity.propertiesByName[@"flightCode"] ];

请注意,returnsDistinctResults 仅在设置了 propertiesToFetch 时有效,propertiesToFetch 仅在 resultTypeNSDictionaryResultType 时有效。

修订

如果您想要完整的 Flight 对象,但每个不同的航班代码只有一个,我认为您不能直接这样做。也许您可以同时要求对象 ID 和航班代码,按航班代码分组,并取最小对象 ID,以便为每个航班代码获取一个对象 ID。然后,您可以在托管对象上下文中使用 objectForID: 将这些对象 ID 一个一个地转换为完整对象。我会尝试这样的事情:

NSEntityDescription *entity = [NSEntityDescription entityForName:@"Flight"
    inManagedObjectContext:moc];

NSExpressionDescription *objectIDProperty = [[NSExpressionDescription alloc] init];
objectIDProperty.name = @"objectID";
objectIDProperty.expression = [NSExpression expressionForFunction:@"min:"
    arguments:@[ [NSExpression expressedForEvaluatedObject] ]];
objectIdProperty.expressionResultType = NSObjectIDAttributeType;

NSAttributeDescription *flightCodeProperty = entity.propertiesByName[@"flightCode"];
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entity.name];
request.resultType = NSDictionaryResultType;
request.returnsDistinctResults = YES;
request.propertiesToFetch = @[ flightCodeProperty, objectIDProperty ];

request.propertiesToGroupBy = @[ flightCodeProperty ];

我从this answer 那里抄袭了很多。我不知道它是否有效,或者我是否在正确的轨道上。如果它完全运行,但没有给出正确的输出,请记住,您可以通过添加 -com.apple.CoreData.SQLDebug 1 作为命令行参数来查看它正在执行的 SQL。

【讨论】:

我在 fetchedResultsController 中使用了这个 fetch 请求,我知道我会失去它的大部分功能(比如观察控制器的变化),但是如果可以的话,是否可以直接在 fetchedResultsController 中调用 objectForID ,如何? 据我所知,这是不可能的。您必须将objectIDPropertyexpressionResultType 设置为使其返回完整对象的东西,但there is no such type。 如果获取所有航班并在代码中过滤成本太高,您可能不得不对模型进行非规范化。例如,为Flight 实体提供一个名为flightCodeRepresentative 的布尔属性,并确保无论何时添加或删除航班,对于每个航班代码,只有一个航班将其设置为true。然后每个航班代码准确地获取一个航班变得微不足道。 使用 RestKit 映射器自动将航班保存到持久存储的问题,是的,使用代码过滤有点贵 您可以使用单个 NSFetchRequest 预填充缓存。缓存只是一个包含所有已知航班代码的NSMutableSet(因为可能每个航班代码至少有一个航班)。插入新航班时,请检查缓存中的航班代码。如果缓存中缺少航班代码,请在新航班上将 flightCodeRepresentative 属性设置为 true 并将代码添加到缓存中。【参考方案2】:

您需要为所有航班创建一个 NSMuteableArray,然后单独创建一个 NSMuteableArray 以跟踪您已经看到的元素

伪代码:

NSMuteablearray flights
NSMuteablearray alreadySeen


for (item in flights) 
if (alreadySeen containsObject:item)
     flights removeObject:item
else
     alreadySeen addObject:item


【讨论】:

我不是在问objective-c 我是在问一个谓词,顺便说一句,获取请求将在一个fetchedResultsController getter中

以上是关于通过属性过滤掉相似对象的谓词的主要内容,如果未能解决你的问题,请参考以下文章

谓词下推

核心数据谓词根据日期属性过滤对象

过滤掉子类结果的 NSPredicate

在 UUID 类型属性上使用 @FetchRequest 谓词过滤进行不可靠更新?

如何对实体的自定义属性进行谓词

带有核心数据对象的 NSPredicate