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 userId
s, [1, 4, 3, 5]
. And I would like to create a NSSortDescriptor
that sorts my User
objects based on the order of the userId
s in the array.
Is that possible, and how should I do that?
Update
I have now tried the following.
I added a transformable property to my User object, where I store the user ids array.
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?
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;
}];
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
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];
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With