How can I do a case insensitive sort of NSFetchedResultsController that also ignores words like "the"?
I'm working on a Core Data driven app that has 2 entities requiring special sorting considerations in the NSFetchedResultsController
.
The simpler case of the 2 just requires ignoring "The" so that (& I'm citing real data examples) The Planting Festival follows Peats Ridge Festival in the sort order. As a safety I'd also like to make that sort case insensitive. For that one I tried the suggestions here How can I sort an NSTableColumn of NSStrings ignoring "The " and "A "? by adding...
@property (nonatomic, readonly) NSString * sortname;
to the interface of my entity definition class &...
- (NSString *)sortname
{
NSMutableString *temp = [NSMutableString stringWithString:[[self name] lowercaseString]];
if ([temp hasPrefix:@"the "]) {
return [temp substringFromIndex:4];
}
return temp;
}
to the implementation but the app crashes when I give it @"sortname"
as the key for the NSSortDescriptor
.
The 2nd entity is more complicated. In addition to stripping "The" for sorting there is also "A" & honorifics like "Dr" & "Professor" but not always. An example in data is Professor Ian Lowe & Professor Wallace's Puppet Theatre. Ian Lowe is a real person who (much as I respect him) should be sorted under I for Ian but there is no Professor Wallace, that's just the name on a, well, puppet theatre & it should be sorted under P. For that selective aspect of it I see no way other than having a boolean attribute on the entity specifies whether or not to strip the 1st word for sorting purposes.
For the rest of it the question comes down to... Is there a way to create a (for want of knowing a more correct term) virtual attribute on a Core Data entity that will work when I pass its name as the key for an NSSortDescriptor
of an NSFetchedResultsController
?
The desperate approach, which I'd rather not take, would be to create real sortname
attributes on those 2 entities & calculate the values as I populate the entities.
Update... Attempting to follow TechZen's answer this is the constructor for my NSFetchedResultsController...
- (NSFetchedResultsController *)frc
{
if (_frc) return _frc;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"EventProfile"
inManagedObjectContext:_moc];
[request setEntity:entity];
NSSortDescriptor *sorter = [[NSSortDescriptor alloc] initWithKey:@"name"
ascending:YES
开发者_运维问答 comparator:^NSComparisonResult(id obj1, id obj2) {
NSMutableString *sortname1 = [NSMutableString stringWithString:[obj1 lowercaseString]];
if ([sortname1 hasPrefix:@"the "]) {
sortname1 = [NSMutableString stringWithString:[sortname1 substringFromIndex:4]];
}
NSMutableString *sortname2 = [NSMutableString stringWithString:[obj2 lowercaseString]];
if ([sortname2 hasPrefix:@"the "]) {
sortname2 = [NSMutableString stringWithString:[sortname2 substringFromIndex:4]];
}
return [sortname1 compare:sortname2];
}];
[request setSortDescriptors:[NSArray arrayWithObject:sorter]];
[sorter release], sorter = nil;
[request setFetchBatchSize:15];
NSFetchedResultsController *frcTemp = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:_moc
sectionNameKeyPath:nil
cacheName:[NSString GetUUID]];
[self setFrc:frcTemp];
[_frc setDelegate:self];
[frcTemp release], frcTemp = nil;
[request release], request = nil;
if ([[_frc fetchedObjects] count] == 0) {
//<#statements#>
}
return _frc;
}
However it is still sorting by name. I inserted an "I'm here." log line in the comparator block & that didn't appear in my logs leading me to think the comparator isn't even being executed.
Cheers & TIA, Pedro :)
NSSortDescriptor does not accept transient property. But there is a work around.
In NSFetchedResultsController
set "sectionNameKeyPath" as your filter, and keep the NSSortDescriptor as the real column found in your core data. So your code would be:
NSFetchedResultsController *fetcher = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:context
sectionNameKeyPath:@"sortname"
cacheName:nil];
You probably want to use a sort descriptor with a block or selector (function) e.g.
+[NSSortDescriptor sortDescriptorWithKey:ascending:comparator:]
With a block or selector, you can create as complex and customized a sort as you need.
You can also use multiple sorts in the same fetch request just remember that the sorts are evaluated in array sequence.
精彩评论