it-swarm.com.ru

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

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

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

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

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

1233
rustyshelf

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

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

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

2267
Georg Schölly

Смотрите метод NSMutableArraysortUsingFunction:context:

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

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

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

107
Alex Reynolds

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

61
Chris

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

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

28
leviathan

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

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

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

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

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

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

18
Hardik Darji

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

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

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

16
freespace

блоки 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
Kostiantyn Sokolinskyi

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

7
DenTheMan

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

//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];
6
MD Aslam Ansari

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

[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
Dinesh_

Я использовал 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
roocell

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

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

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

// сортируем значения

    [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
Boobalan
-(NSMutableArray*) sortArray:(NSMutableArray *)toBeSorted 
{
  NSArray *sortedArray;
  sortedArray = [toBeSorted sortedArrayUsingComparator:^NSComparisonResult(id a, id b) 
  {
    return [a compare:b];
 }];
 return [sortedArray mutableCopy];
}
3
Emile Khattar

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

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

Сортировка 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
Mohit

Протоколы и функциональное программирование Swift делают это очень простым, вам просто нужно привести свой класс в соответствие с протоколом Comparable, реализовать методы, требуемые протоколом, а затем использовать отсортированную (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)
1
James Rochabrun

Сортировка с использованием 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://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/

1
Aamir

В моем случае я использую "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
Emre Gürses

Используйте как это для вложенных объектов,

NSSortDescriptor * sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"lastRoute.to.lastname" ascending:YES selector:@selector(caseInsensitiveCompare:)];
NSMutableArray *sortedPackages = [[NSMutableArray alloc]initWithArray:[packages sortedArrayUsingDescriptors:@[sortDescriptor]]];

lastRoute - это один объект, и этот объект содержит объект to, этот объект содержит строковые значения lastname.

0
Sureshkumar Linganathan

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

0
Syed Qamar Abbas

Вы должны создать 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)
0
Parth Barot