Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSSortDescriptor - Sort descriptor based on another array

I hava a core data application. And I want to fetch one kind of object, User. User have the property userId.

I have another array with userIds, [1, 4, 3, 5]. And I would like to create a NSSortDescriptor that sorts my Userobjects based on the order of the userIds in the array.

Is that possible, and how should I do that?

Update

I have now tried the following.

  1. I added a transformable property to my User object, where I store the user ids array.

  2. I have tried the following sort descriptor:

    sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"userId" ascending:YES     comparator:^NSComparisonResult(id obj1, id obj2) {
        NSUInteger idx1 = [self.user.followingIds indexOfObject:[obj1 valueForKey:@"userId"]];
        NSUInteger idx2 = [self.user.followingIds indexOfObject:[obj2 valueForKey:@"userId"]];
        return idx1 - idx2;
    }];
    

And I'm getting the following error:

Serious application error.  Exception was caught during Core Data change processing.  This  is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification.   [<__NSCFNumber 0xa337400> valueForUndefinedKey:]: this class is not key value coding-compliant  for the key userId. with userInfo {
    NSTargetObjectUserInfoKey = 2502;
    NSUnknownUserInfoKey = userId;
}
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason:    '[<__NSCFNumber 0xa337400> valueForUndefinedKey:]: this class is not key value coding-   compliant for the key userId.'

Update 2

Would also like to add a relationship Followers between User objects. Any ideas how that relationship should look? See attached image. Is that correct?

enter image description here

like image 777
Anders Avatar asked May 13 '13 07:05

Anders


3 Answers

This can not be done with a sort descriptor, you have to apply a custom comparator function after fetching the results:

NSArray *userIds = ...; // e.g. @[@1, @4, @3, @5]
NSArray *results = ...; // result of fetch request
NSArray *sorted = [results sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
    NSUInteger idx1 = [userIds indexOfObject:[obj1 valueForKey:@"userId"]];
    NSUInteger idx2 = [userIds indexOfObject:[obj2 valueForKey:@"userId"]];
    return idx1 - idx2;
}];
like image 194
Martin R Avatar answered Oct 17 '22 01:10

Martin R


As @MartinR said, you cannot do that with sort descriptors.
To achieve a scalable solution in O(N) complexity (if you really need it):

//Not tested
NSArray* permutation = ...;//@[@1,@4,@3,@5] (user ids);
NSMutableDictionary* map = [[NSMutableDictionary alloc] initWithCapacity:[permutation count]];
for (NSUInteger i =0; i< [permutation count]; ++i) {
    map[permutation[i]] = @(i);
}
NSMutableArray* results = ...;//A mutable copy of the results
for (NSUInteger i = 0; i < [permutation count];) {
    id userId = [results[i] valueForKey:@"userId"];
    NSUInteger key = [map[userId] unsignedIntegerValue];//where we like this object to be
    if (key != i) {
        [results exchangeObjectAtIndex:i withObjectAtIndex:key];
    } else {
        ++i;
    }
}

Analysis: (Please correct me if you spot any mistakes)

The mapping stage takes N operation to complete: O(N)
The array copy takes N operations: O(N)
The loop iterate N times, but:
it perform at max N exchanges
and at max N comparisons
hence: O(N) + O(N) + O(N) + O(N) = 4*O(N) = O(N)

@MartinR solution will be based on a comparison based sort limited to a minimum O(N*lg(N)) operations.
however each comparison takes O(N) to get the first index and O(N) to get the second index.
hence: O(N*lg(N))*(O(N) + O(N)) = 2*O(N)*O(N*lg(N)) = O((N^2)*lg(N))

For small enough N there is no real difference (N=10 ==> ~40 operations for my solution and ~200 operations for Martin's solution).
For large N Martin's solutions will not scale well:
N == 1000 ==> ~4000 operations for my solution and ~2*1000*1000*8 =~ 16*10^6

like image 22
Dan Shelly Avatar answered Oct 17 '22 00:10

Dan Shelly


Use this idea which i use to sort message id if it is added to DB

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"message_id" ascending:NO selector:@selector(localizedStandardCompare:)];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
    [request setSortDescriptors:sortDescriptors];
    request.returnsObjectsAsFaults=NO;
 NSError *error = nil;
    NSArray *results = [managedObjectContext executeFetchRequest:request error:&error];
like image 38
iEinstein Avatar answered Oct 17 '22 01:10

iEinstein