Как отсортировать NSMutableArray с пользовательскими объектами в нем?

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

Я думаю, что это имеет какое-то отношение к этому методу:

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

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

1111
задан 30 апр. '09 в 9:10
источник поделиться
22 ответов

Метод сравнения

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

- (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, щелкнув строку заголовка.

2156
ответ дан 30 апр. '09 в 9:25
источник

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

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

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

Эта функция возвращает NSComparisonResult: она возвращает NSOrderedAscending, если PersonA.birthDate < 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;
}

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

97
ответ дан 30 апр. '09 в 9:24
источник

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

Также вам нужно решить, что делать, если элементы вашего массива не соответствуют типу, для этого примера я только что вернул 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: Это сортировка в порядке убывания.

53
ответ дан 25 марта '11 в 1:15
источник

Начиная с 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];

Примечание: "отсортированный" массив будет автореализован.

24
ответ дан 16 марта '11 в 21:46
источник

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

Это работает как прелесть:

NSSortDescriptor *sorter = [[NSSortDescriptor alloc] initWithKey:@"crimeScene.distance" ascending:YES];
[self.arrAnnotations sortUsingDescriptors:[NSArray arrayWithObject:sorter]];
24
ответ дан 22 нояб. '10 в 13:51
источник

В отсутствует второй ответ Георга Шёлли, но он отлично работает.

NSSortDescriptor *sortDescriptor;
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"birthDate"
                                              ascending:YES] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray;
sortedArray = [drinkDetails sortedArrayUsingDescriptor:sortDescriptors];
17
ответ дан 06 мая '09 в 19:25
источник
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"birthDate" ascending:YES] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray;
sortedArray = [drinkDetails sortedArrayUsingDescriptors:sortDescriptors];

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

16
ответ дан 30 марта '11 в 8:54
источник

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

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

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

16
ответ дан 30 апр. '09 в 9:20
источник

Блоки 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 немного описания

8
ответ дан 14 апр. '11 в 12:03
источник

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

7
ответ дан 09 нояб. '10 в 16:47
источник

Если вы просто сортируете массив NSNumbers, вы можете отсортировать их с помощью 1 вызова:

[arrayToSort sortUsingSelector: @selector(compare:)];

Это работает, потому что объекты в массиве (NSNumber objects) реализуют метод сравнения. Вы можете сделать то же самое для объектов NSString или даже для массива пользовательских объектов данных, реализующих метод сравнения.

Вот пример кода с использованием блоков компаратора. Он сортирует массив словарей, где каждый словарь содержит число в ключе "sort_key".

#define SORT_KEY @\"sort_key\"

[anArray sortUsingComparator: 
 ^(id obj1, id obj2) 
  {
  NSInteger value1 = [[obj1 objectForKey: SORT_KEY] intValue];
  NSInteger value2 = [[obj2 objectForKey: SORT_KEY] intValue];
  if (value1 > value2) 
{
  return (NSComparisonResult)NSOrderedDescending;
  }

  if (value1 < value2) 
{
  return (NSComparisonResult)NSOrderedAscending;
  }
    return (NSComparisonResult)NSOrderedSame;
 }];

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

 #define SORT_KEY @\"sort_key\"

[anArray sortUsingComparator: 
^(id obj1, id obj2) 
 {
  NSNumber* key1 = [obj1 objectForKey: SORT_KEY];
  NSNumber* key2 = [obj2 objectForKey: SORT_KEY];
  return [key1 compare: key2];
 }];

или тело компаратора может быть даже перегоняется до 1 строки:

  return [[obj1 objectForKey: SORT_KEY] compare: [obj2 objectForKey: SORT_KEY]];

Я предпочитаю простые операторы и множество временных переменных, потому что код легче читать и проще отлаживать. Компилятор в любом случае оптимизирует временные переменные, поэтому нет преимущества для версии "все в одной строке".

5
ответ дан 06 дек. '12 в 10:51
источник

Вы можете использовать следующий общий метод для своей цели. Он должен решить вашу проблему.

//Called method
-(NSMutableArray*)sortArrayList:(NSMutableArray*)arrDeviceList filterKeyName:(NSString*)sortKeyName ascending:(BOOL)isAscending{
    NSSortDescriptor *sorter = [[NSSortDescriptor alloc] initWithKey:sortKeyName ascending:isAscending];
    [arrDeviceList sortUsingDescriptors:[NSArray arrayWithObject:sorter]];
    return arrDeviceList;
}

//Calling method
[self sortArrayList:arrSomeList filterKeyName:@"anything like date,name etc" ascending:YES];
4
ответ дан 16 апр. '15 в 12:17
источник

Я использовал sortUsingFunction:: в некоторых моих проектах:

int SortPlays(id a, id b, void* context)
{
    Play* p1 = a;
    Play* p2 = b;
    if (p1.score<p2.score) 
        return NSOrderedDescending;
    else if (p1.score>p2.score) 
        return NSOrderedAscending;
    return NSOrderedSame;
}

...
[validPlays sortUsingFunction:SortPlays context:nil];
4
ответ дан 09 мая '11 в 2:27
источник

Я создал небольшую библиотеку методов категорий, называемую Linq to ObjectiveC, что делает эту вещь более легкой. Используя метод sort с помощью селектора клавиш, вы можете сортировать по birthDate следующим образом:

NSArray* sortedByBirthDate = [input sort:^id(id person) {
    return [person birthDate];
}]
4
ответ дан 06 марта '13 в 11:29
источник

Я просто выполнил многоуровневую сортировку на основе пользовательских требований.

//сортировать значения

    [arrItem sortUsingComparator:^NSComparisonResult (id a, id b){

    ItemDetail * itemA = (ItemDetail*)a;
    ItemDetail* itemB =(ItemDetail*)b;

    //item price are same
    if (itemA.m_price.m_selling== itemB.m_price.m_selling) {

        NSComparisonResult result=  [itemA.m_itemName compare:itemB.m_itemName];

        //if item names are same, then monogramminginfo has to come before the non monograme item
        if (result==NSOrderedSame) {

            if (itemA.m_monogrammingInfo) {
                return NSOrderedAscending;
            }else{
                return NSOrderedDescending;
            }
        }
        return result;
    }

    //asscending order
    return itemA.m_price.m_selling > itemB.m_price.m_selling;
}];

https://sites.google.com/site/greateindiaclub/mobil-apps/ios/multilevelsortinginiosobjectivec

4
ответ дан 04 июня '13 в 13:09
источник
-(NSMutableArray*) sortArray:(NSMutableArray *)toBeSorted 
{
  NSArray *sortedArray;
  sortedArray = [toBeSorted sortedArrayUsingComparator:^NSComparisonResult(id a, id b) 
  {
    return [a compare:b];
 }];
 return [sortedArray mutableCopy];
}
3
ответ дан 10 янв. '13 в 18:37
источник

Сортировка NSMutableArray очень проста:

NSMutableArray *arrayToFilter =
     [[NSMutableArray arrayWithObjects:@"Photoshop",
                                       @"Flex",
                                       @"AIR",
                                       @"Flash",
                                       @"Acrobat", nil] autorelease];

NSMutableArray *productsToRemove = [[NSMutableArray array] autorelease];

for (NSString *products in arrayToFilter) {
    if (fliterText &&
        [products rangeOfString:fliterText
                        options:NSLiteralSearch|NSCaseInsensitiveSearch].length == 0)

        [productsToRemove addObject:products];
}
[arrayToFilter removeObjectsInArray:productsToRemove];
2
ответ дан 06 сент. '11 в 12:44
источник
NSSortDescriptor  *sort = [[NSSortDescriptor alloc] initWithKey:@"_strPrice"
                                                 ascending:sortFlag selector:@selector(localizedStandardCompare:)] ;
0
ответ дан 05 янв. '15 в 10:00
источник

Быстрые протоколы и функциональное программирование очень упрощают вам просто сделать свой класс совместимым с протоколом Comparable, реализовать методы, требуемые протоколом, а затем использовать отсортированную (по:) функцию высокого порядка для создания отсортированного массива без кстати, нужно использовать изменяемые массивы.

class Person: Comparable {
    var birthDate: NSDate?
    let name: String

    init(name: String) {
        self.name = name
    }

    static func ==(lhs: Person, rhs: Person) -> Bool {
        return lhs.birthDate === rhs.birthDate || lhs.birthDate?.compare(rhs.birthDate as! Date) == .orderedSame
    }

    static func <(lhs: Person, rhs: Person) -> Bool {
        return lhs.birthDate?.compare(rhs.birthDate as! Date) == .orderedAscending
    }

    static func >(lhs: Person, rhs: Person) -> Bool {
        return lhs.birthDate?.compare(rhs.birthDate as! Date) == .orderedDescending
    }

}

let p1 = Person(name: "Sasha")
p1.birthDate = NSDate() 

let p2 = Person(name: "James")
p2.birthDate = NSDate()//he is older by miliseconds

if p1 == p2 {
    print("they are the same") //they are not
}

let persons = [p1, p2]

//sort the array based on who is older
let sortedPersons = persons.sorted(by: {$0 > $1})

//print sasha which is p1
print(persons.first?.name)
//print James which is the "older"
print(sortedPersons.first?.name)
0
ответ дан 28 марта '17 в 1:14
источник

Сортировка с использованием NSComparator

Если мы хотим сортировать пользовательские объекты, нам нужно предоставить NSComparator, который используется для сравнения пользовательских объектов. Блок возвращает значение NSComparisonResult для обозначения порядка двух объектов. Поэтому для сортировки целого массива NSComparator используется следующим образом.

NSArray *sortedArray = [employeesArray sortedArrayUsingComparator:^NSComparisonResult(Employee *e1, Employee *e2){
    return [e1.firstname compare:e2.firstname];    
}];

Сортировка с использованием NSSortDescriptor
Предположим в качестве примера, что у нас есть массив, содержащий экземпляры пользовательского класса, Employee имеет атрибуты firstname, lastname и age. В следующем примере показано, как создать NSSortDescriptor, который может использоваться для сортировки содержимого массива в возрастающем порядке по возрастному ключу.

NSSortDescriptor *ageDescriptor = [[NSSortDescriptor alloc] initWithKey:@"age" ascending:YES];
NSArray *sortDescriptors = @[ageDescriptor];
NSArray *sortedArray = [employeesArray sortedArrayUsingDescriptors:sortDescriptors];

Сортировка с использованием пользовательских сопоставлений
Имена являются строками, и когда вы сортируете строки для представления пользователю, вы всегда должны использовать локализованное сравнение. Часто вы также хотите провести сравнение без учета регистра. Вот пример с (localizedStandardCompare:) для упорядочивания массива по имени и имени.

NSSortDescriptor *lastNameDescriptor = [[NSSortDescriptor alloc]
              initWithKey:@"lastName" ascending:YES selector:@selector(localizedStandardCompare:)];
NSSortDescriptor * firstNameDescriptor = [[NSSortDescriptor alloc]
              initWithKey:@"firstName" ascending:YES selector:@selector(localizedStandardCompare:)];
NSArray *sortDescriptors = @[lastNameDescriptor, firstNameDescriptor];
NSArray *sortedArray = [employeesArray sortedArrayUsingDescriptors:sortDescriptors];

Для справки и подробного обсуждения см. https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/SortDescriptors/Articles/Creating.html
http://www.ios-blog.co.uk/tutorials/objective-c/how-to-sort-nsarray-with-custom-objects/

0
ответ дан 16 июня '16 в 12:32
источник

В моем случае я использую sortedArrayUsingComparator для сортировки массива. Посмотрите на приведенный ниже код.

contactArray = [[NSArray arrayWithArray:[contactSet allObjects]] sortedArrayUsingComparator:^NSComparisonResult(ContactListData *obj1, ContactListData *obj2) {
    NSString *obj1Str = [NSString stringWithFormat:@"%@ %@",obj1.contactName,obj1.contactSurname];
    NSString *obj2Str = [NSString stringWithFormat:@"%@ %@",obj2.contactName,obj2.contactSurname];
    return [obj1Str compare:obj2Str];
}];

Также мой объект,

@interface ContactListData : JsonData
@property(nonatomic,strong) NSString * contactName;
@property(nonatomic,strong) NSString * contactSurname;
@property(nonatomic,strong) NSString * contactPhoneNumber;
@property(nonatomic) BOOL isSelected;
@end
0
ответ дан 13 дек. '16 в 15:22
источник
NSMutableArray *stockHoldingCompanies = [NSMutableArray arrayWithObjects:fortune1stock,fortune2stock,fortune3stock,fortune4stock,fortune5stock,fortune6stock , nil];

NSSortDescriptor *sortOrder = [NSSortDescriptor sortDescriptorWithKey:@"companyName" ascending:NO];

[stockHoldingCompanies sortUsingDescriptors:[NSArray arrayWithObject:sortOrder]];

NSEnumerator *enumerator = [stockHoldingCompanies objectEnumerator];

ForeignStockHolding *stockHoldingCompany;

NSLog(@"Fortune 6 companies sorted by Company Name");

    while (stockHoldingCompany = [enumerator nextObject]) {
        NSLog(@"===============================");
        NSLog(@"CompanyName:%@",stockHoldingCompany.companyName);
        NSLog(@"Purchase Share Price:%.2f",stockHoldingCompany.purchaseSharePrice);
        NSLog(@"Current Share Price: %.2f",stockHoldingCompany.currentSharePrice);
        NSLog(@"Number of Shares: %i",stockHoldingCompany.numberOfShares);
        NSLog(@"Cost in Dollars: %.2f",[stockHoldingCompany costInDollars]);
        NSLog(@"Value in Dollars : %.2f",[stockHoldingCompany valueInDollars]);
    }
    NSLog(@"===============================");
-2
ответ дан 29 окт. '13 в 4:28
источник

Другие вопросы по меткам или Задайте вопрос