如何对包含自定义对象的NSMutableArray进行排序?

我想做的事情看起来很简单,但我在网上找不到任何答案。我有一个NSMutableArray的对象,假设它们是'Person'对象。我想按Person.birth日期对NSMutableArray进行排序,这是一个NSDate

我认为这与这种方法有关:

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

在Java我将使我的对象实现可比较,或使用Collections.sort与内联自定义比较器……你到底是如何在Objective-C中做到这一点的?

481671 次浏览

您的Person对象需要实现一个方法,例如compare:接受另一个Person对象,并根据2个对象之间的关系返回NSComparisonResult

然后你会用@selector(compare:)调用sortedArrayUsingSelector:,它应该完成。

还有其他方法,但据我所知,没有Comparable接口的Cocoa等价。使用sortedArrayUsingSelector:可能是最轻松的方法。

NSMutableArray方法#1

您需要设置一个比较函数,它接受两个对象(类型为Person,因为您正在比较两个Person对象)和一个背景参数。

这两个对象只是Person的实例。第三个对象是一个字符串,例如@“出生日期”。

此函数返回NSComparisonResult:如果PersonA.birthDate<PersonB.birthDate,它将返回NSOrderedAscending。如果PersonA.birthDate>PersonB.birthDate,它将返回NSOrderedDescending。最后,如果PersonA.birthDate==PersonB.birthDate,它将返回NSOrderedSame

这是粗略的伪代码;你需要充实一个日期“更少”、“更多”或“等于”另一个日期的含义(例如比较秒自纪元等):

NSComparisonResult compare(Person *firstPerson, Person *secondPerson, void *context) {if ([firstPerson birthDate] < [secondPerson birthDate])return NSOrderedAscending;else if ([firstPerson birthDate] > [secondPerson birthDate])return NSOrderedDescending;elsereturn NSOrderedSame;}

如果你想要更紧凑的东西,你可以使用三元运算符:

NSComparisonResult compare(Person *firstPerson, Person *secondPerson, void *context) {return ([firstPerson birthDate] < [secondPerson birthDate]) ? NSOrderedAscending : ([firstPerson birthDate] > [secondPerson birthDate]) ? NSOrderedDescending : NSOrderedSame;}

如果你经常这样做,内联可能会加快一点速度。

比较方法

你可以为你的对象实现一个比较方法:

- (NSComparisonResult)compare:(Person *)otherObject {return [self.birthDate compare:otherObject.birthDate];}
NSArray *sortedArray = [drinkDetails sortedArrayUsingSelector:@selector(compare:)];

NSSortDetptor(更好)

或者通常更好:

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

您可以通过向数组添加多个键来轻松按多个键排序。也可以使用自定义比较器方法。看看留档

积木(闪亮!)

自Mac OS X 10.6和iOS4以来,也可以使用块进行排序:

NSArray *sortedArray;sortedArray = [drinkDetails sortedArrayUsingComparator:^NSComparisonResult(Person *a, Person *b) {return [a.birthDate compare:b.birthDate];}];

性能

一般来说,-compare:和基于块的方法比使用NSSortDescriptor快得多,因为后者依赖于KVC。NSSortDescriptor方法的主要优点是它提供了一种使用数据而不是代码定义排序顺序的方法,这使得设置事情变得容易,因此用户可以通过单击标题行对NSTableView进行排序。

Georg Schölly的第二个回答中缺少一个步骤,但它可以正常工作。

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

//添加了's',因为我复制和粘贴时浪费了时间,并且在sortedArrayUsing描述符中没有's'时失败

对于NSMutableArray,使用sortUsingSelector方法。它对它进行排序,而不创建新实例。

我尝试了所有,但这对我有用。在一个类中,我有另一个名为“crimeScene”的类,并希望按“crimeScene”的属性进行排序。

这就像一个魅力:

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

从iOS4开始,您还可以使用块进行排序。

对于这个特定的例子,我假设数组中的对象有一个“位置”方法,它返回一个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];

注意:“排序”数组将被自动释放。

我在iOS4中使用了一个块。必须将数组的元素从id转换为我的类类型。在这种情况下,它是一个名为Score的类,具有一个名为point的属性。

如果数组的元素不是正确的类型,您还需要决定该怎么办,对于这个例子,我刚刚返回了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:这是降序排序。

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

谢谢,工作正常…

iOS4块将节省你:)

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;elsereturn NSOrderedDescending;}else // eSeatQualityRed || eSeatQualityYellow{if ( first.quality > second.quality )return NSOrderedAscending;elsereturn NSOrderedDescending;}}}] retain];

http://sokol8.blogspot.com/2011/04/sorting-nsarray-with-blocks.html一点描述

我在我的一些项目中使用了::

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非常简单:

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:fliterTextoptions:NSLiteralSearch|NSCaseInsensitiveSearch].length == 0)
[productsToRemove addObject:products];}[arrayToFilter removeObjectsInArray:productsToRemove];

如果您只是对NSNumbers的数组进行排序,您可以使用1次调用对它们进行排序:

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

我更喜欢简单的语句和大量的临时变量,因为代码更容易阅读,也更容易调试。编译器无论如何都会优化掉临时变量,所以对所有功能于一行的版本没有优势。

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

我创建了一个名为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 sameif (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 itemif (result==NSOrderedSame) {
if (itemA.m_monogrammingInfo) {return NSOrderedAscending;}else{return NSOrderedDescending;}}return result;}
//asscending orderreturn itemA.m_price.m_selling > itemB.m_price.m_selling;}];

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

您可以使用以下通用方法来解决您的问题。

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

使用NS比较器排序

如果我们想对自定义对象进行排序,我们需要提供NSComparator,它用于比较自定义对象。该块返回一个NSComparisonResult值来表示两个对象的顺序。因此,为了对整个数组进行排序,NSComparator以以下方式使用。

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

使用NSSortDetptor排序
让我们假设,作为一个例子,我们有一个包含自定义类实例的数组,员工有属性first name、lastname和age。以下示例说明了如何创建一个NSSortExplptor,该描述符可用于按age键升序对数组内容进行排序。

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

使用自定义比较进行排序
名称是字符串,当您对字符串进行排序以呈现给用户时,您应该始终使用本地化比较。通常您还希望执行不区分大小写的比较。这是一个使用(localizedStandard ardCompare:)按姓氏和名字对数组进行排序的示例。

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/

在我的例子中,我使用“sortedArrayUsing比较器”对数组进行排序。请看下面的代码。

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的协议和函数式编程使这变得非常容易你只需要让你的类符合比较协议,实现协议所需的方法,然后使用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 olderlet sortedPersons = persons.sorted(by: {$0 > $1})
//print sasha which is p1print(persons.first?.name)//print James which is the "older"print(sortedPersons.first?.name)

Swift中的排序数组


对于Swifty Person下面是一个非常干净的技术,可以在全球范围内实现上述目标。让我们有一个示例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}}

现在让我们为User添加一个extension ofArray。简单地说,让我们仅为那些只有User对象的数组添加一些方法。

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

升序的用法

let sortedArray = someArray.sortUserByDate(.orderedAscending)

降序的用法

let sortedArray = someArray.sortUserByDate(.orderedAscending)

同一订单的用法

let sortedArray = someArray.sortUserByDate(.orderedSame)

只有当Array是类型时,extension中的上述方法才可访问[User]||Array<User>

您必须创建sortDetptor,然后您可以使用sortDetptor对nsmutablearray进行排序,如下所示。

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

您使用NSSortDetptor对具有自定义对象的NSMutableArray进行排序

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

像这样用于嵌套对象,

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

lastRoute是一个对象,该对象包含to对象,to对象包含lastname字符串值。

Swift版本:5.1

如果你有一个自定义结构或类并且想要对它们进行任意排序,你应该使用对你指定的字段进行排序的尾随闭包调用sor()。这是一个使用对特定属性进行排序的自定义结构数组的示例:

    struct User {var firstName: String}
var users = [User(firstName: "Jemima"),User(firstName: "Peter"),User(firstName: "David"),User(firstName: "Kelly"),User(firstName: "Isabella")]
users.sort {$0.firstName < $1.firstName}

如果你想返回一个已排序的数组而不是就地对其进行排序,请像这样使用sorted():

    let sortedUsers = users.sorted {$0.firstName < $1.firstName}
  let sortedUsers = users.sorted {$0.firstName < $1.firstName}