Сортировать объекты по переведенным свойствам

У меня есть запрос на выборку, который возвращает объекты с английскими свойствами. Я хочу отсортировать эти объекты по их переведенным (русским) эквивалентам, используя NSLocalizedString. Мой код неправильно возвращает данные, отсортированные, как если бы они были английскими. Я использую следующий дескриптор сортировки:

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

Моя сущность Core Data называется Good со свойствами category и name. Мне нужен массив дескрипторов сортировки, который я могу передать контроллеру выборки (setSortDescriptors:), который будет сортировать сначала по переведенной категории, а затем по переведенному имени.


Если языковой стандарт английский, а слова на английском, сортировка допустима. Но если языковой стандарт русский или украинский, сортировка не работает. Это uotput для отсортированного массива для русской локали

Кулеры
Кулеры
Память
Память
Микрофоны
Блоки питания
Блоки питания
Звуковые карты
Видеокарты
Видеокарты

и это неправильно. Что я делаю не так? Спасибо!

Обновление 1. Я тестирую его

for (int i = 0; i < sortedArray.count; i++)
{

    NSLog(@"%@", NSLocalizedString([[sortedArray objectAtIndex: i] category], nil));
}

Обновление 2, весь код

- (NSFetchedResultsController *) fetchedResultsController
{
    if (_fetchedResultsController != nil)
    {
        return _fetchedResultsController;
    }
    NSManagedObjectContext *context = [self managedObjectContext];

    NSFetchRequest *goodsFetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *goodsEntity = [NSEntityDescription entityForName:@"Goods" inManagedObjectContext:context];

    [goodsFetchRequest setEntity: goodsEntity];
    NSError * error = nil;

    NSArray * goods = [_managedObjectContext executeFetchRequest: goodsFetchRequest error:&error];

    NSSortDescriptor *categoryDescriptor = [[NSSortDescriptor alloc] initWithKey: @"category" ascending: YES ];
    NSSortDescriptor *nameDescriptor = [[NSSortDescriptor alloc] initWithKey: @"name" ascending: YES selector: @selector(localizedCaseInsensitiveCompare:)];

    NSArray *sortedArray = [goods sortedArrayUsingDescriptors:
                            [NSArray arrayWithObject:
                             [NSSortDescriptor sortDescriptorWithKey: @"category"
                                                           ascending:YES
                                                            selector:@selector(localizedCaseInsensitiveCompare:)]]];




       NSLog(@"------");
        for (int i = 0; i < sortedArray.count; i++)
       {

          NSLog(@"%@", NSLocalizedString([[sortedArray objectAtIndex: i] category], nil));
       }

    NSArray *sortDescriptors = @[categoryDescriptor, nameDescriptor];

    [goodsFetchRequest setSortDescriptors: sortDescriptors];


    _fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest: goodsFetchRequest managedObjectContext: context sectionNameKeyPath:@"category" cacheName: nil];

    _fetchedResultsController.delegate = self;


    return _fetchedResultsController;
}

person SergStav    schedule 30.03.2015    source источник
comment
Можете выложить поподробнее, как вы это тестируете? Я не могу воспроизвести это. Он сортирует Блоки питания, Блоки питания, Видеокарты, Видеокарты, Звуковые карты, ... (что я считаю правильным) в моих тестах на Mac и iOS как на английском, так и на русском языках. Вы уверены, что выводите sortedArray, а не goods?   -  person Rob Napier    schedule 30.03.2015


Ответы (1)


Использование localizedCaseInsensitiveCompare: просто означает, что при рассмотрении вопроса о том, является ли одна заданная строка меньше другой (т.е. эквивалентны ли «Ф» и «ф» для целей «нечувствительности к регистру», будут проводиться консультации с локалью). Это не заставляет систему вызывать NSLocalizedString по категории за вас.

Моим предпочтительным решением было бы добавить переведенные свойства к вашему объекту модели:

@interface Good (Translated)
@property (nonatomic, readonly) NSString *translatedCategory;
@property (nonatomic, readonly) NSString *translatedName;
@end

@implementation Good (Translated)    
- (NSString *)translatedCategory {
    return NSLocalizedString(self.category, nil);
}

- (NSString *)translatedName {
    return NSLocalizedString(self.name, nil);
}
@end

Затем вы можете отсортировать и отобразить эти переведенные свойства. Мне нравится этот подход, потому что я думаю, что он делает код очень ясным и означает, что вы можете реализовать кеширование или что-то еще, если хотите, или добавить другую логику, зависящую от свойств. Тем не менее, могут быть случаи, когда вы предпочтете сортировать по переводу. Или, может быть, у вас их много, и вы хотите избежать создания тонны шаблонов (или причудливых трюков с пересылкой методов).

В этом случае вы можете использовать собственный дескриптор сортировки со своим собственным правилом сравнения. Для простого NSArray вы можете использовать дескриптор сортировки на основе компаратора, но Core Data не поддерживает блоки, поэтому нам понадобится специальный селектор, который мы можем вызвать.

Нам нужна функция сравнения, которая сравнивает переведенные строки. Мы можем сделать это, добавив категорию в NSString следующим образом:

@interface NSString (MYTranslatedSorting)
- (NSComparisonResult)my_translatedCaseInsensitiveCompare:(NSString *)other;
@end

@implementation NSString (MYTranslatedSorting)

- (NSComparisonResult)my_translatedCaseInsensitiveCompare:(NSString *)other {
    NSString *translatedSelf = NSLocalizedString(self, nil);
    NSString *translatedOther = NSLocalizedString(other, nil);
    return [translatedSelf localizedCaseInsensitiveCompare:translatedOther];
}
@end

Теперь можно вызвать [englishString my_translatedCaseInsensitiveCompare:otherEnglish] и вернуть заказ на основе переведенной версии. При этом мы можем использовать следующие дескрипторы сортировки:

NSArray *sortDescriptors =
@[
  [NSSortDescriptor sortDescriptorWithKey:@"category" ascending:YES selector:@selector(my_translatedCaseInsensitiveCompare:)],
  [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES selector:@selector(my_translatedCaseInsensitiveCompare:)],
  ];
person Rob Napier    schedule 30.03.2015
comment
Так действительно ли массив goods на английском или на русском? Весь ваш код не соответствует вашему тестированию с помощью кода. (В последнем есть NSLocalizedString.) Список, который вы дали, отсортирован по английскому (я думаю: охлаждающие вентиляторы, память, микрофоны, ...), что заставляет меня думать, что goods на английском. - person Rob Napier; 30.03.2015
comment
Извините, я отредактировал. На самом деле массив товаров на английском языке, но у меня есть localizable.strings, где у меня есть перевод для всех слов. На самом деле я хочу использовать эту категорию в качестве имен разделов tableView. - person SergStav; 30.03.2015
comment
Верно; вам просто нужно локализовать строки, прежде чем сравнивать их. См. Выше. - person Rob Napier; 30.03.2015
comment
Если я просто скопирую и вставлю этот код, мое приложение выйдет из строя с [Goods localizedCompare:]: неопознанный селектор отправлен в экземпляр 0x7fae5bf0f760 ' - person SergStav; 30.03.2015
comment
Код сортирует строки, а не объекты товаров. См. Выше. Вы не должны копировать и вставлять его. Стоит набрать и подумать, что он делает. - person Rob Napier; 30.03.2015
comment
Да, я понимаю, но когда я копирую этот код, мое приложение вылетает - person SergStav; 30.03.2015
comment
Понял; Я отредактировал его, чтобы читать категории, а не просто сортировать строку. - person Rob Napier; 30.03.2015
comment
Теперь работает, спасибо. Но не могли бы вы помочь мне применить это к моей категории дескриптор, пожалуйста? - person SergStav; 30.03.2015
comment
См. +sortDescriptorWithKey:ascending:comparator:. Это должно позволить вам создать дескриптор сортировки, который работает так, как вы хотите. - person Rob Napier; 30.03.2015