2

I set my NSFetchRequests's sortDescriptor to an array that contains only one NSSortDescriptor:

[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES
                  selector:@selector(localizedCaseInsensitiveCompare:)]

Then, I create a NSFetchResultsController that also has @"name" as its sectionKeyNamePath.

When the locale is English, then letters with diacritical marks are sorted together with their plain letters. For instance, “Å” is treated as “A” for the purpose of sorting. The problem is that this is not taken into account when the fetch results are broken up into sections, so every time the sequence of results changes from “A” to ”Å” or vice versa, a new section is added. Consequently, the NSFetchResultsController's sectionIndexTitles returns an array

("\U00c5", A, "\U00c5", A, "\U00c5", A, B, C, …)

and the section index titles look like this in my UITableView:

Section index titles

Question: How can I make it so that names that start with letters that sort together only result in a single section?

Removing diacritical marks from the underlying data that are used to sort is not an option because some other locales treat ”A” and “Å” as separate letters, with “Å” sorted after “Z” in the alphabet.

A NSSortDescriptor with a custom comparator is not an option because Core Data can only use a fixed set of built-in comparators. (This is because they are being translated to SQL for the underlying database.)

Vebjorn Ljosa
  • 17,438
  • 13
  • 70
  • 88

1 Answers1

0

This SO question and answer may hold the key...

[NSSortDescriptor sortDescriptorWithKey:@"name" 
                              ascending:YES
                             comparator:(NSComparator)^(NSString *key1, NSString *key2) {
    return [key1 compare:key2 
                 options:NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch];    
}];

While I am unable to test this ATM, in theory this should work.

The comparator should iterate through the sortDescriptorWithKey:@"name" and compare each one against the other to return a set of letters both case and diacritic insensitive.

Hope this helps.

Community
  • 1
  • 1
andrewbuilder
  • 3,629
  • 2
  • 24
  • 46
  • 2
    You cannot use block-based sort descriptors in a *Core Data* fetch request. Only sort descriptors using a (persistent) attribute are allowed, and only a fixed set of built-in comparator methods (such as localizedCaseInsensitiveCompare). – Martin R May 04 '14 at 12:42
  • Right, @MartinR; I should maybe have mentioned that in my question. Adding it. – Vebjorn Ljosa May 04 '14 at 12:48
  • Ah thanks @MartinR. If I can determine an alternative I'll update my response. – andrewbuilder May 04 '14 at 13:48
  • @VebjornLjosa, have you thought about creating a separate attribute (e.g. `sectionName`) for your entity, and using that with your `sectionNameKeyPath` in your FRC? When each entity is created (or checked each time the entity is called) you'd need to assess the first letter of the attribute `name` to determine the value for (my example) `sectionName`. – andrewbuilder May 04 '14 at 14:02
  • @andrewbuilder, do you mean to set the new attribute to the first letter with diacritical marks removed? That would maybe work for the English locale because the English locale seems to sort with something like NSDiacriticInsensitiveSearch, but it's not a general solution that will work in other locales. I could maybe special case it (because most of my users use only a couple of locales), but it would be ugly. It seems like this is a common problem that every app with an address book will encounter, so I hope that a general solution exists. – Vebjorn Ljosa May 04 '14 at 14:18