Как мне отсортировать NSMutableArray с пользовательскими объектами в нем? - PullRequest
1234 голосов
/ 30 апреля 2009

То, что я хочу сделать, кажется довольно простым, но я не могу найти ответы в Интернете. У меня есть NSMutableArray объектов, и скажем, они являются объектами «Персона». Я хочу отсортировать NSMutableArray по Person.birthDate, который является NSDate.

Я думаю, что это как-то связано с этим методом:

NSArray *sortedArray = [drinkDetails sortedArrayUsingSelector:@selector(???)];

В Java я бы заставил мой объект реализовать Comparable или использовал Collections.sort со встроенным пользовательским компаратором ... как же это сделать в Objective-C?

Ответы [ 26 ]

2269 голосов
/ 30 апреля 2009

Сравнить метод

Либо вы реализуете метод сравнения для вашего объекта:

- (NSComparisonResult)compare:(Person *)otherObject {
    return [self.birthDate compare:otherObject.birthDate];
}

NSArray *sortedArray = [drinkDetails sortedArrayUsingSelector:@selector(compare:)];

NSSortDescriptor (лучше)

или обычно даже лучше:

NSSortDescriptor *sortDescriptor;
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"birthDate"
                                           ascending:YES];
NSArray *sortedArray = [drinkDetails sortedArrayUsingDescriptors:@[sortDescriptor]];

Вы можете легко отсортировать по нескольким ключам, добавив более одного в массив. Использование пользовательских методов сравнения также возможно. Посмотрите документацию .

Блоки (блестящие!)

Существует также возможность сортировки по блокам начиная с Mac OS X 10.6 и iOS 4:

NSArray *sortedArray;
sortedArray = [drinkDetails sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
    NSDate *first = [(Person*)a birthDate];
    NSDate *second = [(Person*)b birthDate];
    return [first compare:second];
}];

Производительность

Методы -compare: и основанные на блоках будут в целом немного быстрее, чем использование NSSortDescriptor, так как последний использует KVC. Основное преимущество метода NSSortDescriptor заключается в том, что он обеспечивает способ определения порядка сортировки с использованием данных, а не кода, что позволяет легко, например, настройте параметры так, чтобы пользователи могли сортировать NSTableView, щелкая строку заголовка.

107 голосов
/ 30 апреля 2009

См. NSMutableArray метод sortUsingFunction:context:

Вам необходимо настроить функцию сравнить , которая принимает два объекта (типа Person, поскольку вы сравниваете два объекта Person) и параметр context .

Два объекта являются просто экземплярами Person. Третий объект - это строка, например, @ "РОЖДЕНИЕ".

Эта функция возвращает NSComparisonResult: возвращает NSOrderedAscending, если PersonA.birthDate <<code>PersonB.birthDate. Он вернет NSOrderedDescending, если PersonA.birthDate> PersonB.birthDate. Наконец, он вернет NSOrderedSame, если PersonA.birthDate == PersonB.birthDate.

Это грубый псевдокод; вам нужно будет уточнить, что означает, что одна дата «меньше», «больше» или «равна» другой дате (например, сравнение секунд с момента начала и т. д.):

NSComparisonResult compare(Person *firstPerson, Person *secondPerson, void *context) {
  if ([firstPerson birthDate] < [secondPerson birthDate])
    return NSOrderedAscending;
  else if ([firstPerson birthDate] > [secondPerson birthDate])
    return NSOrderedDescending;
  else 
    return NSOrderedSame;
}

Если вы хотите что-то более компактное, вы можете использовать троичные операторы:

NSComparisonResult compare(Person *firstPerson, Person *secondPerson, void *context) {
  return ([firstPerson birthDate] < [secondPerson birthDate]) ? NSOrderedAscending : ([firstPerson birthDate] > [secondPerson birthDate]) ? NSOrderedDescending : NSOrderedSame;
}

Встраивание может немного ускорить это, если вы делаете это много.

61 голосов
/ 25 марта 2011

Я сделал это в iOS 4, используя блок. Пришлось привести элементы моего массива из id к типу моего класса. В данном случае это был класс Score с свойством points.

Также вам нужно решить, что делать, если элементы вашего массива не того типа, для этого примера я только что возвратил NSOrderedSame, однако в моем коде я исключение.

NSArray *sorted = [_scores sortedArrayUsingComparator:^(id obj1, id obj2){
    if ([obj1 isKindOfClass:[Score class]] && [obj2 isKindOfClass:[Score class]]) {
        Score *s1 = obj1;
        Score *s2 = obj2;

        if (s1.points > s2.points) {
            return (NSComparisonResult)NSOrderedAscending;
        } else if (s1.points < s2.points) {
            return (NSComparisonResult)NSOrderedDescending;
        }
    }

    // TODO: default is the same?
    return (NSComparisonResult)NSOrderedSame;
}];

return sorted;

PS: сортировка по убыванию.

28 голосов
/ 16 марта 2011

Начиная с iOS 4, вы также можете использовать блоки для сортировки.

Для этого конкретного примера я предполагаю, что у объектов в вашем массиве есть метод 'position', который возвращает NSInteger.

NSArray *arrayToSort = where ever you get the array from... ;
NSComparisonResult (^sortBlock)(id, id) = ^(id obj1, id obj2) 
{
    if ([obj1 position] > [obj2 position]) 
    { 
        return (NSComparisonResult)NSOrderedDescending;
    }
    if ([obj1 position] < [obj2 position]) 
    {
        return (NSComparisonResult)NSOrderedAscending;
    }
    return (NSComparisonResult)NSOrderedSame;
};
NSArray *sorted = [arrayToSort sortedArrayUsingComparator:sortBlock];

Примечание: «отсортированный» массив будет автоматически освобожден.

25 голосов
/ 22 ноября 2010

Я перепробовал все, но у меня это сработало. В классе у меня есть другой класс с именем "crimeScene", и я хочу отсортировать по свойству "crimeScene".

Это работает как шарм:

NSSortDescriptor *sorter = [[NSSortDescriptor alloc] initWithKey:@"crimeScene.distance" ascending:YES];
[self.arrAnnotations sortUsingDescriptors:[NSArray arrayWithObject:sorter]];
19 голосов
/ 06 мая 2009

Во втором ответе Георгия Шолли отсутствует шаг , но тогда он работает нормально.

NSSortDescriptor *sortDescriptor;
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"birthDate"
                                              ascending:YES] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray;
sortedArray = [drinkDetails sortedArrayUsingDescriptor:sortDescriptors];
18 голосов
/ 30 марта 2011
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"birthDate" ascending:YES] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray;
sortedArray = [drinkDetails sortedArrayUsingDescriptors:sortDescriptors];

Спасибо, все отлично работает ...

16 голосов
/ 30 апреля 2009

Ваши Person объекты должны реализовать метод, скажем compare:, который принимает другой Person объект, и возвращает NSComparisonResult в соответствии с отношением между этими двумя объектами.

Тогда вы бы позвонили sortedArrayUsingSelector: с @selector(compare:), и это должно быть сделано.

Существуют и другие способы, но, насколько я знаю, нет какао-эквивалента интерфейса Comparable. Использование sortedArrayUsingSelector:, вероятно, самый безболезненный способ сделать это.

8 голосов
/ 14 апреля 2011

Блоки iOS 4 спасут вас:)

featuresArray = [[unsortedFeaturesArray sortedArrayUsingComparator: ^(id a, id b)  
{
    DMSeatFeature *first = ( DMSeatFeature* ) a;
    DMSeatFeature *second = ( DMSeatFeature* ) b;

    if ( first.quality == second.quality )
        return NSOrderedSame;
    else
    {
        if ( eSeatQualityGreen  == m_seatQuality || eSeatQualityYellowGreen == m_seatQuality || eSeatQualityDefault  == m_seatQuality )
        {
            if ( first.quality < second.quality )
                return NSOrderedAscending;
            else
                return NSOrderedDescending;
        }
        else // eSeatQualityRed || eSeatQualityYellow
        {
            if ( first.quality > second.quality )
                return NSOrderedAscending;
            else
                return NSOrderedDescending;
        } 
    }
}] retain];

http://sokol8.blogspot.com/2011/04/sorting-nsarray-with-blocks.html немного описания

7 голосов
/ 09 ноября 2010

Для NSMutableArray используйте метод sortUsingSelector. Он сортирует это место, не создавая новый экземпляр.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...