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

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

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

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

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

вопрос задан 30.04.2009
rustyshelf
25043 репутация

25 ответов


  • 2224 рейтинг

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

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

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

    ответ дан Georg Schölly, с репутацией 96137, 30.04.2009
  • 103 рейтинг

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

    .

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

    Два объекта являются только экземплярами Person. Третий объект - это строка, т.е. г. @"Дата рождения".

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

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

    ответ дан Alex Reynolds, с репутацией 66007, 30.04.2009
  • 58 рейтинг

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

    ответ дан Chris, с репутацией 856, 24.03.2011
  • 28 рейтинг

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

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

    ответ дан leviathan, с репутацией 9859, 16.03.2011
  • 24 рейтинг

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

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

    NSSortDescriptor *sorter = [[NSSortDescriptor alloc] initWithKey:@"crimeScene.distance" ascending:YES];
    [self.arrAnnotations sortUsingDescriptors:[NSArray arrayWithObject:sorter]];
    
    ответ дан Fernando Redondo, с репутацией 850, 22.11.2010
  • 18 рейтинг
    NSSortDescriptor *sortDescriptor;
    sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"birthDate" ascending:YES] autorelease];
    NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
    NSArray *sortedArray;
    sortedArray = [drinkDetails sortedArrayUsingDescriptors:sortDescriptors];
    

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

    ответ дан Hardik Darji, с репутацией 189, 30.03.2011
  • 16 рейтинг

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

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

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

    ответ дан freespace, с репутацией 13149, 30.04.2009
  • 8 рейтинг

    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. ком / 2011/04 / сортировочно-NSArray-с-блоков. HTML немного описания

    ответ дан Kostiantyn Sokolinskyi, с репутацией 686, 14.04.2011
  • 7 рейтинг

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

    ответ дан DenTheMan, с репутацией 865, 9.11.2010
  • 6 рейтинг

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

    //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];
    
    ответ дан MD Aslam Ansari, с репутацией 1141, 16.04.2015
  • 5 рейтинг

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

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

    Это работает, потому что объекты в массиве (NSNumber объекты) реализуют метод сравнения. Вы можете сделать то же самое для объектов 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]];
    

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

    ответ дан Dinesh_, с репутацией 164, 6.12.2012
  • 4 рейтинг

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

    int SortPlays(id a, id b, void* context)
    {
        Play* p1 = a;
        Play* p2 = b;
        if (p1.scorep2.score) 
            return NSOrderedAscending;
        return NSOrderedSame;
    }
    
    ...
    [validPlays sortUsingFunction:SortPlays context:nil];
    
    ответ дан roocell, с репутацией 2009, 8.05.2011
  • 4 рейтинг

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

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

    ответ дан ColinE, с репутацией 53142, 6.03.2013
  • 4 рейтинг

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

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

        [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. ru / site / greateindiaclub / мобильные приложения / ios / многоуровневая сортировкаiniosobjectivec

    ответ дан Boobalan, с репутацией 627, 4.06.2013
  • 3 рейтинг
    -(NSMutableArray*) sortArray:(NSMutableArray *)toBeSorted 
    {
      NSArray *sortedArray;
      sortedArray = [toBeSorted sortedArrayUsingComparator:^NSComparisonResult(id a, id b) 
      {
        return [a compare:b];
     }];
     return [sortedArray mutableCopy];
    }
    
    ответ дан Emile Khattar, с репутацией 319, 10.01.2013
  • 2 рейтинг

    Сортировка 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];
    
    ответ дан Mohit, с репутацией 61, 6.09.2011
  • 1 рейтинг

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

    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)
    
    ответ дан James Rochabrun, с репутацией 751, 27.03.2017
  • 1 рейтинг

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

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

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

    Сортировка с использованием NSSortDescriptor
    Предположим, в качестве примера, что у нас есть массив, содержащий экземпляры пользовательского класса, у Employee есть атрибуты имя, фамилия и возраст. В следующем примере показано, как создать 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: // разработчик. яблоко. ком / библиотека / ИОС / документация / Какао / Концептуальные / SortDescriptors / Статьи / Создание. HTML
    http: // www. ИОС-блог. сотрудничество. uk / руководства / target-c / как сортировать nsarray-with-custom-objects /

    ответ дан Aamir, с репутацией 6978, 16.06.2016
  • 0 рейтинг

    В моем случае я использую «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
    
    ответ дан Emre Gürses, с репутацией 438, 13.12.2016
  • 0 рейтинг

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

     NSSortDescriptor *sortingDescriptor;
     sortingDescriptor = [[NSSortDescriptor alloc] initWithKey:@"birthDate"
                                           ascending:YES];
     NSArray *sortArray = [drinkDetails sortedArrayUsingDescriptors:@[sortDescriptor]];
    
    ответ дан Arvind Patel, с репутацией 1, 14.06.2018
  • 0 рейтинг

    Сортировать массив в 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 из 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

    ответ дан Syed Qamar Abbas, с репутацией 2140, 24.03.2018
  • 0 рейтинг

    Вы должны создать 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)
    
    ответ дан Parth Barot, с репутацией 103, 12.04.2018
  • -1 рейтинг
    NSSortDescriptor  *sort = [[NSSortDescriptor alloc] initWithKey:@"_strPrice"
                                                     ascending:sortFlag selector:@selector(localizedStandardCompare:)] ;
    
    ответ дан does_not_exist, с репутацией , 5.01.2015
  • -3 рейтинг
    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(@"===============================");
    
    ответ дан Siddhesh Bondre, с репутацией 437, 29.10.2013