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?

3
  • 1
    You can refer to stackoverflow.com/questions/805547/… and stackoverflow.com/questions/1066829/… Commented Jan 30, 2012 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 Commented Feb 1, 2012 at 12:21
  • Is there a way to do it without adding the extra variable? Commented Feb 1, 2012 at 15:46

3 Answers 3

38
+250

You can use sort descriptors and KVC collection operators:

NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"[email protected]" ascending:YES];

For example, in a CoreData fetch:

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

NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"[email protected]" 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:@"[email protected]" ascending:YES];

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

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

Sign up to request clarification or add additional context in comments.

3 Comments

if you are using Core Data this is the way to go... +1
Just a little explanation: NSSortDescriptors access the value by which they are sorting using Key-Value-Coding. So you can use the key "[email protected]" 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
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.
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];
}

2 Comments

This works for me perfectly as I am not using CoreData. Thanks Artanis
NSSortDescriptor and CoreData are not tied. You can use sortUsingDescriptors: array method to sort elements with NSSortDescriptor.
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]];
}

2 Comments

I'm getting an 'index 0 beyond bounds for empty array' error for this part of the return statement: [sortedApp1 objectAtIndex:0]
This would be very slow, since it would require pulling into memory all appointment objects.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.