Как мне отсортировать NSmutableArray с настраиваемыми объектами?

StackOverflow https://stackoverflow.com/questions/805547

Вопрос

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

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

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

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

Это было полезно?

Решение

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

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

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

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

НССортДескриптор (лучше)

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

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 объекты) и контекст параметр.

Эти два объекта являются просто экземплярами 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;
}

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

Я сделал это в 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;

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

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

В этом конкретном примере я предполагаю, что объекты в вашем массиве имеют метод позиции, который возвращает 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];

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

Я перепробовал все, но это сработало для меня.В классе у меня есть еще один класс с именем "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 объект и вернуть NSComparisonResult в зависимости от отношений между двумя объектами.

Тогда бы ты позвонил 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, вы можете отсортировать их одним вызовом:

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

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

Я создал небольшую библиотеку методов категорий под названием Linq для 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];
}

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

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

Сортировка 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
Предположим, в качестве примера, что у нас есть массив, содержащий экземпляры пользовательского класса, «Сотрудник» имеет атрибуты «имя», «фамилия» и «возраст».В следующем примере показано, как создать 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/

Протоколы 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)

В моем случае я использую 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 из 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 * sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"lastRoute.to.lastname" ascending:YES selector:@selector(caseInsensitiveCompare:)];
NSMutableArray *sortedPackages = [[NSMutableArray alloc]initWithArray:[packages sortedArrayUsingDescriptors:@[sortDescriptor]]];

LastRoute — это один объект, и этот объект содержит объект to, а этот объект — строковые значения фамилии.

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(@"===============================");
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top