11

(sorry about the long title)

I have a custom object Person, which in turn has an NSSet which has several custom objects called Appointment. A Person therefore can have several appointments. Appointment has the values startTime and endTime.

These are Core Data NSMangagedObject classes.

@interface Person : NSManagedObject

@property (nonatomic, retain) NSString *personName;
@property (nonatomic, retain) NSSet *appointments;

// etc

@end


@interface Appointment : NSManagedObject

@property (nonatomic, retain) NSNumber * startSecond;
@property (nonatomic, retain) NSNumber * endSecond;

// etc

@end

How would I get a list of Persons, in order of the earliest startSecond within any of their appointments?

Venk
  • 5,949
  • 9
  • 41
  • 52
cannyboy
  • 24,180
  • 40
  • 146
  • 252
  • 1
    You can refer to http://stackoverflow.com/questions/805547/how-to-sort-an-nsmutablearray-with-custom-objects-in-it and http://stackoverflow.com/questions/1066829/what-is-the-most-efficient-way-to-sort-an-nsset – Amresh Kumar Jan 30 '12 at 11:41
  • 1
    You should probably make a custom compare function that first fetches the earliest of dates of the appointments of a person. (If you add each appointment one by one every time you could probably keep a variable that holds the earliest time anyway, just check the new appointment with the latest lowest of times there). Then you can simply compare: each person and their earliest date with each other with a simple date comparison. This should be doable for NSManagedObjects as well since they inherit from NSObject. I could probably write you an example but first let me know if this is what you seek – Totumus Maximus Feb 01 '12 at 12:21
  • Is there a way to do it without adding the extra variable? – cannyboy Feb 01 '12 at 15:46

3 Answers3

38

You can use sort descriptors and KVC collection operators:

NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"appointments.@min.startSecond" ascending:YES];

For example, in a CoreData fetch:

NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"Person"];

NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"appointments.@min.startSecond" ascending:YES];
[request setSortDescriptors:@[sortDescriptor]];

NSError *error = nil;
NSArray *sortedResults = [context executeFetchRequest:request error:&error];

Or just sorting an array:

NSArray *people = @[...];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"appointments.@min.startSecond" ascending:YES];

NSArray *sortedPeople = [people sortedArrayUsingDescriptors:@[sortDescriptor]];

More information on KVC collection operators can be found in the KVC Programming Guide.

Ell Neal
  • 6,014
  • 2
  • 29
  • 54
  • if you are using Core Data this is the way to go... +1 – Alex Stanciu Feb 01 '12 at 23:18
  • 1
    Just a little explanation: NSSortDescriptors access the value by which they are sorting using Key-Value-Coding. So you can use the key "appointments.@min.startSecond" which basically means: in the collection appointments, get the startSecond of the object, whose startSecond is the smallest among the objects in the collection. then it just sorts by the returned value – Ahti Feb 02 '12 at 01:09
  • This king of descriptors aren't allowed for NSFetchRequest ("Keypath containing KVC aggregate where there shouldn't be one" error). But accessible for fetched result. – HotJard Nov 03 '15 at 12:49
2

If you have the data in an NSArray form you can sort it like this:

NSArray *sortedPersonArray = [coreDataPersonArray sortedArrayUsingSelector:@selector(compare:)];

- (NSComparisonResult)compare:(Person *)personObject {
    return [self.startSecond compare:personObject.startSecond];
}
Alex Stanciu
  • 5,183
  • 2
  • 24
  • 31
  • This works for me perfectly as I am not using CoreData. Thanks Artanis – iOSAppDev May 28 '13 at 06:29
  • `NSSortDescriptor` and `CoreData` are not tied. You can use [`sortUsingDescriptors:`](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSMutableArray_Class/index.html#//apple_ref/occ/instm/NSMutableArray/sortUsingDescriptors:) array method to sort elements with `NSSortDescriptor`. – DanSkeel Feb 16 '15 at 12:49
1

A suggestion:

// Sorting key
NSString *key = @"startSecond";

// A mutable array version of your list of Persons.
NSMutableArray *a = [NSMutableArray arrayWithObjects:Person1, Person2, Person3, nil];

// Then use the sorted appointements to get your sorted person array.
[a sortUsingComparator:^NSComparisonResult(Person *p1, Person *p2) {
    NSSortDescriptor *sortDesc1 = [NSSortDescriptor sortDescriptorWithKey:key ascending:NO];
    NSArray *sortedApp1 = [p1.appointements sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDesc1]];

    NSSortDescriptor *sortDesc2 = [NSSortDescriptor sortDescriptorWithKey:key ascending:NO];
    NSArray *sortedApp2 = [p2.appointements sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDesc2]];

    return [[[sortedApp1 objectAtIndex:0] valueForKey:key] compare:[[sortedApp2 objectAtIndex:0] valueForKey:key]];
}
onekiloparsec
  • 2,013
  • 21
  • 32
  • I'm getting an 'index 0 beyond bounds for empty array' error for this part of the return statement: [sortedApp1 objectAtIndex:0] – cannyboy Feb 01 '12 at 15:44
  • This would be very slow, since it would require pulling into memory all appointment objects. – Daniel Eggert Feb 03 '12 at 08:21