1

I"m performing a regular NSFetchRequest without any predicate to fetch 100 managed objects (flights), each Flight entity has an attribute of type NSString named (flightCode) and this attribute is not unique, so 2 flight objects may have the same flightCode . However, I want to fetch all flight objects filtering out flights that have the same flightCode by taking only one flight from similarities, i.e.

if the fetch request returned 5 flights as follows:

flight1: flightCode = ABC

flight2: flightCode = AA

flight3: flightCode = ABC

flight4: flightCode = ABC

flight5: flightCode = DEF

then the fetch request must filter out any two of the 3 flights that have the flightCode ABC and take only any random one of these 3.

what is the required NSPredicate for this filtering ?

p.s. flights: 1, 3 & 4 may be different in other attributes, i.e. flight1's name may be different from flight3's name.

thanks in advance.

JAHelia
  • 6,934
  • 17
  • 74
  • 134
  • Are you looking for the unique flight codes or do you want a random `Flight` instance from each set of equal flight codes? – Barry Wark Jun 25 '13 at 21:42
  • @BarryWark I'm looking for a random Flight instance from each set of equal flight codes. – JAHelia Jun 26 '13 at 01:05
  • I don't think you can accomplish what you want with an `NSFetchRequest`. What you're looking for is some variation of a group-by operation. In Core Data, that has to be done in-memory. Looks like you're using an `NSArrayController`. – Barry Wark Jun 26 '13 at 04:34
  • @BarryWark can you elaborate more or show a piece of code ? – JAHelia Jun 26 '13 at 07:12

2 Answers2

1

If you just want a list of all distinct flight codes, this may do what you want:

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

Note that returnsDistinctResults only works if propertiesToFetch is set, and propertiesToFetch only works if resultType is NSDictionaryResultType.

REVISED

If you want full Flight objects, but only one for each distinct flight code, I don't think you can do that directly. Perhaps you can ask for both the object ID and the flight code, group by flight code, and take the minimum object ID, to get one object ID for each flight code. Then you can turn those object IDs into full objects one by one using objectForID: on the managed object context. I would try something like this:

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 ];

I cribbed a lot of that from this answer. I have no idea if it works, or if I'm even on the right track. If it runs at all, but doesn't quite give the right output, remember that you can see the SQL it's executing by adding -com.apple.CoreData.SQLDebug 1 as a command-line argument.

Community
  • 1
  • 1
rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • I'm using this fetch request inside a fetchedResultsController, i know that I will lose most of its functionality (like observing for controller changes) but is it possible to call objectForID inside the fetchedResultsController directly, if yes, HOW ? – JAHelia Jun 26 '13 at 06:00
  • As far as I know, it's not possible. You'd have to set the `expressionResultType` of `objectIDProperty` to something that makes it return a full object, but [there is no such type](http://developer.apple.com/library/ios/#DOCUMENTATION/Cocoa/Reference/CoreDataFramework/Classes/NSAttributeDescription_Class/reference.html#//apple_ref/doc/c_ref/NSAttributeType). – rob mayoff Jun 26 '13 at 06:02
  • If fetching all the flights and filtering in code is too expensive, you'll probably have to denormalize your model. For example, give the `Flight` entity a boolean attribute named `flightCodeRepresentative`, and make sure whenever you add or remove flights that exactly one flight has it set to true for each flight code. Then fetching exactly one flight per flight code becomes trivial. – rob mayoff Jun 26 '13 at 06:05
  • the problem that flights are saved to the persistent store automatically with RestKit mapper, and yes, filtering with code is kinda expensive – JAHelia Jun 26 '13 at 06:09
  • do you advise me to write a direct sqlite query for this case ? – JAHelia Jun 26 '13 at 06:15
  • You should not access a Core Data store directly with sqlite. If you want to try to denormalize your model in the way that I suggested, and you need help doing it, you should post a question to that effect, explaining exactly how you are using RestKit to update your store. – rob mayoff Jun 26 '13 at 06:17
  • .. make sure whenever you add or remove flights that exactly one flight has it .. this means that I have to check at each insert that the current Flight's flightCode exists among other flights records, isn't this expensive too at the inserting level ? – JAHelia Jun 26 '13 at 06:22
  • 1
    You can pre-populate an cache with a single `NSFetchRequest`. The cache is simply an `NSMutableSet` containing all known flight codes (since presumably each flight code has at least one flight). When you insert a new flight, check the cache for its flight code. If the cache lacks the flight code, set the `flightCodeRepresentative` property to true on the new flight and add the code to the cache. – rob mayoff Jun 26 '13 at 06:31
  • after i commented out objectIDProperty Expression Description the run time error disappears, it sounds that i can't run more than 1 expression at the same fetch request ? – JAHelia Jun 26 '13 at 08:39
  • I suspect the problem is simply that the `min:` function cannot be applied to object IDs, and that you will have to go with a different solution like the denormalization I suggested earlier. – rob mayoff Jun 26 '13 at 08:46
  • yes exactly it's the min function does not apply to object IDs, I've started a new SO question about checking against a condition at saving time with RestKit. here: http://stackoverflow.com/questions/17333424/restkit-0-2x-checking-for-a-condition-at-inserting-time – JAHelia Jun 27 '13 at 05:59
  • Good idea. Unfortunately I have no experience with RestKit. Good luck. – rob mayoff Jun 27 '13 at 06:23
0

youll want to make a NSMuteableArray for all of your flights and then a separate NSMuteableArray to keep track of which elements you have already seen

pseudocode:

NSMuteablearray flights
NSMuteablearray alreadySeen


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

}
Ben
  • 609
  • 11
  • 19
  • I'm not asking objective-c I'm asking about a predicate, by the way the fetch request will be inside a fetchedResultsController getter – JAHelia Jun 25 '13 at 20:50