4

I can't wrap my head around how to do arbitrary sorting with a NSSortDescriptor.

I want to do something like this:

NSArray *sortAlgorithm = [NSArray arrayWithObjects:@"@", @"#", @"!", @"&", @"r", @"a", nil];

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" 
                                                               ascending:YES 
                                                              comparator:
    ^(id obj1, id obj2) {
        NSComparisonResult comparisonResult;
        //Some code that uses sortAlgorithm.
        return comparisonResult;
    }
];

This would sort the objects by the key name so that any key that starts with @, e.g. @home, would come before any key that starts with r, e.g. radical, and that again would come before any key that starts with a, e.g. anything.

The above is just an example. The point is to enable completely arbitrary sorting.

This is to be used for a NSFetchedResultsController.

What would the code for //Some code that uses sortAlgorithm look like?

EDIT:

Code surrounding my attempt to implement a sortDescriptor, as pr. occulus' suggestion:

- (NSFetchedResultsController *)fetchedResultsController {
    if (__fetchedResultsController)
        return __fetchedResultsController;

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

    fetchRequest.entity = [NSEntityDescription entityForName:@"Tag" inManagedObjectContext:self.temporaryManagedObjectContext];

    //NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:NO];
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:NO comparator:^(id obj1, id obj2) {

        NSArray *sortAlgorithm = [NSArray arrayWithObjects:@"#", @"!", @"@", @".", nil];

        NSString *obj1FirstChar = [(NSString *)obj1 substringToIndex:1];
        NSString *obj2FirstChar = [(NSString *)obj2 substringToIndex:1];
        int idx1 = [sortAlgorithm indexOfObject:obj1FirstChar];
        int idx2 = [sortAlgorithm indexOfObject:obj2FirstChar];

        if ( idx1 < idx2 )
            return NSOrderedAscending;
        else if ( idx1 > idx2 )
            return NSOrderedDescending;
        else
            return NSOrderedSame;
    }];

    fetchRequest.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
    fetchRequest.fetchBatchSize = 20;        

    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.temporaryManagedObjectContext sectionNameKeyPath:nil cacheName:@"Tags"];

    aFetchedResultsController.delegate = self;
    self.fetchedResultsController = aFetchedResultsController;

    [self.fetchedResultsController performFetch:nil];

    return __fetchedResultsController;
}

The commented-out sortDescriptor works.

There is definitely a property called name on objects of entity "Tag". But even if there weren't, that doesn't seem to be the problem. Xcode doesn't seem to even be compiling that line of code (the sortDescriptor), which sounds ridiculous. Breakpoints are working just fine, but aren't breaking on that specific line of code.

Stian Høiland
  • 3,625
  • 2
  • 27
  • 32
  • 1
    Ran into the same problem recently. I bought Don's *theory* in the end: http://stackoverflow.com/questions/4789782/nsfetchedresultscontroller-custom-sort-not-getting-called#comment5314586_4795667 – Eric Chen May 08 '12 at 04:17

1 Answers1

3

You need to pull out the first characters of obj1 and obj2 strings as NSStrings, find their indexes in your arbitrary ordering array, then compare the positions.

Something like this: (put this code in your ^ block)

NSString *obj1FirstChar = [(NSString *)obj1 substringToIndex:1];
NSString *obj2FirstChar = [(NSString *)obj2 substringToIndex:1];
int idx1 = [sortAlgorithm indexOfObject:obj1FirstChar];
int idx2 = [sortAlgorithm indexOfObject:obj2FirstChar];

// NOTE: we haven't dealt with the case where one of the
// chars wasn't found in the array of characters. A strategy
// for this would need to be decided.

if (idx1 == idx2) {
    return NSOrderedSame;
}
if (idx1 < idx2) {
    return NSOrderedAscending;
}
return NSOrderedDescending;

Not tested, may require a little tweaking. Watch out for upper/lower case character differences.

UPDATE: It seems there are problems with using custom sort descriptors and SQL backed core data stores. Please see this question for more info.

Community
  • 1
  • 1
occulus
  • 16,959
  • 6
  • 53
  • 76
  • I implemented your suggestion, and for testing I limited the source to only include characters found in the sortAlgorithm array. And absolutely nothing happens. Even more nothing than I would expect: I set a breakpoint at the definition of the NSSortDescriptor, and it never breaks. If I set a breakpoint at the instruction right before or after, it breaks. Xcode is simple and terribly inconveniently skipping my attempt to test your solution... I figured I would post this comment while I'm working on solving this new problem incase this is known to someone else. – Stian Høiland Nov 25 '11 at 20:29
  • I believe the algorithm I gave above is correct. There may be a problem with it not even getting used/called, as you note. – occulus Nov 25 '11 at 21:27
  • Can you post the code for how you're actually requesting the sort? i.e. where you put the NSSortDescriptor to use. It sounds like the sort descriptor is never being used. Is there definitely a property on your objects called 'name' and of type NSString *? – occulus Nov 25 '11 at 21:32
  • Updated question with code. And yes, your code looks fine but it seems like the sort descriptor isn't being used. – Stian Høiland Nov 26 '11 at 01:37
  • Hi Stian, it appears that there are known bugs with using core data and custom sort descriptors. I've updated my answer above to reflect this, see the last bit. – occulus Nov 26 '11 at 12:45
  • Interesting, and thanks for being so helpful. Unfortunately the solution suggested in that question is not applicable to my situation. This is really bumming me out as the app is ready to go with the exception of this sorting, which is important functionality. I might have to use my TSI's for this. – Stian Høiland Nov 26 '11 at 13:18
  • Your problem is now outside the scope of this question, by the look of it - maybe open a new one? I think we've got a decent solution to the question you actually asked, so would you consider accepting my answer and opening a new question about your current issue? – occulus Nov 26 '11 at 15:15