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

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

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

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

в Java я бы сделал свой объект сопоставимым или использовал коллекции.сортировка с помощью встроенного пользовательского компаратора...как вы это делаете в Objective-C?

25 ответов


сравнить способ

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

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


посмотреть NSMutableArray метод sortUsingFunction:context:

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


Я сделал это в 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: это сортировка в порядке убывания.


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

Примечание: массив "sorted" будет автоматически выпущен.


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

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

NSSortDescriptor *sorter = [[NSSortDescriptor alloc] initWithKey:@"crimeScene.distance" ascending:YES];
[self.arrAnnotations sortUsingDescriptors:[NSArray arrayWithObject:sorter]];

отсутствует шаг в второй ответ Георга Шелли, но он работает хорошо.

NSSortDescriptor *sortDescriptor;
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"birthDate"
                                              ascending:YES] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray;
sortedArray = [drinkDetails sortedArrayUsingDescriptor:sortDescriptors];

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

спасибо, он работает нормально...


код Person объекты должны реализовать метод, скажем compare: что делает еще один Person объект и return NSComparisonResult согласно отношению между 2 объектами.

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

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


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


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


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

//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];

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

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

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

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

#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]];

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


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

NSArray* sortedByBirthDate = [input sort:^id(id person) {
    return [person birthDate];
}]

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

//сортировка значений

    [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


я использовал 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];

-(NSMutableArray*) sortArray:(NSMutableArray *)toBeSorted 
{
  NSArray *sortedArray;
  sortedArray = [toBeSorted sortedArrayUsingComparator:^NSComparisonResult(id a, id b) 
  {
    return [a compare:b];
 }];
 return [sortedArray mutableCopy];
}

сортировка 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];

сортировка с помощью NSComparator

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

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

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

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/


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

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: { > })

//print sasha which is p1
print(persons.first?.name)
//print James which is the "older"
print(sortedPersons.first?.name)

в моем случае я использую "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

Сортировка Массива В Swift


на Swifty человек ниже очень чистый метод для того чтобы достигнуть вышеуказанной цели для глобально. Давайте иметь пример пользовательского класса User которые имеют некоторые атрибуты.

class User: NSObject {
    var id: String?
    var name: String?
    var email: String?
    var createdDate: Date?
}

теперь у нас есть массив, который нужно сортировать на основе createdDate либо по возрастанию и / или по убыванию. Поэтому давайте добавим функцию для сравнения дат.

class User: NSObject {
    var id: String?
    var name: String?
    var email: String?
    var createdDate: Date?
    func checkForOrder(_ otherUser: User, _ order: ComparisonResult) -> Bool {
        if let myCreatedDate = self.createdDate, let othersCreatedDate = otherUser.createdDate {
            //This line will compare both date with the order that has been passed.
            return myCreatedDate.compare(othersCreatedDate) == order
        }
        return false
    }
}

теперь есть extension of Array на User. В простых словах давайте добавим некоторые методы только для тех массивов, которые имеют только User объекты в нем.

extension Array where Element: User {
    //This method only takes an order type. i.e ComparisonResult.orderedAscending
    func sortUserByDate(_ order: ComparisonResult) -> [User] {
        let sortedArray = self.sorted { (user1, user2) -> Bool in
            return user1.checkForOrder(user2, order)
        }
        return sortedArray
    }
}

использование для восходящего порядка

let sortedArray = someArray.sortUserByDate(.orderedAscending)

использование для нисходящего порядка

let sortedArray = someArray.sortUserByDate(.orderedAscending)

использование для того же

let sortedArray = someArray.sortUserByDate(.orderedSame)

выше способ в extension будет доступен только если Array тип [User]//Array<User>


вы должны создать sortDescriptor, а затем вы можете отсортировать nsmutablearray с помощью sortDescriptor, как показано ниже.

 let sortDescriptor = NSSortDescriptor(key: "birthDate", ascending: true, selector: #selector(NSString.compare(_:)))
 let array = NSMutableArray(array: self.aryExist.sortedArray(using: [sortDescriptor]))
 print(array)

вы используете NSSortDescriptor для сортировки NSMutableArray с пользовательскими объектами

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

NSSortDescriptor  *sort = [[NSSortDescriptor alloc] initWithKey:@"_strPrice"
                                                 ascending:sortFlag selector:@selector(localizedStandardCompare:)] ;

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(@"===============================");