如何sorting一个NSMutableArray中的自定义对象?
我想要做的事似乎很简单,但我无法在网上find任何答案。 我有一个NSMutableArray
的对象,让我们说,他们是'人'的对象。 我想sortingNSMutableArray
由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(更好)
或者通常甚至更好:
NSSortDescriptor *sortDescriptor; sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"birthDate" ascending:YES]; NSArray *sortedArray = [drinkDetails sortedArrayUsingDescriptors:@[sortDescriptor]];
您可以轻松地通过向数组添加多个键来sorting。 使用自定义比较器方法也是可能的。 看看文档 。
块(有光泽!)
自Mac OS X 10.6和iOS 4以来,还可以使用块进行sorting:
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:
和block的方法通常要比使用NSSortDescriptor
快得多,因为后者依赖于KVC。 NSSortDescriptor
方法的主要优点是它提供了一种使用数据而不是代码来定义sorting顺序的方法,这使得用户可以通过点击标题行来sortingNSTableView
。
请参阅NSMutableArray
方法sortUsingFunction:context:
您将需要设置一个比较函数,它使用两个对象( Person
types,因为您正在比较两个Person
对象)和一个上下文参数。
这两个对象只是Person
实例。 第三个对象是一个string,例如@“birthDate”。
这个函数返回一个NSComparisonResult
:如果PersonA.birthDate
< PersonB.birthDate
,则返回PersonB.birthDate
。 如果PersonA.birthDate
> PersonB.birthDate
,它将返回PersonB.birthDate
。 最后,如果PersonA.birthDate
== PersonB.birthDate
将返回PersonB.birthDate
。
这是粗略的伪代码; 你将需要充实一下date的意思是“更less”,“更多”或“相等”到另一个date(比如比较秒等等):
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转换为我的类types。 在这种情况下,它是一个叫Score的类,有一个名为points的属性。
如果数组的元素不是正确的types,你也需要决定该怎么做,对于这个例子,我只是返回NSOrderedSame
,但是在我的代码中,我通过了一个exception。
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:这是降序sorting。
从iOS 4开始,您还可以使用块进行sorting。
对于这个特定的例子,我假设你的数组中的对象有一个'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];
注意:“sorting”数组将被自动释放。
我尝试了所有,但是这为我工作。 在一个类中,我有另一个类名为“ crimeScene
”,并且想要通过“ crimeScene
”属性进行sorting。
这就像一个魅力:
NSSortDescriptor *sorter = [[NSSortDescriptor alloc] initWithKey:@"crimeScene.distance" ascending:YES]; [self.arrAnnotations sortUsingDescriptors:[NSArray arrayWithObject:sorter]];
你的Person
对象需要实现一个方法,比如compare:
它接受另一个Person
对象,并根据这两个对象之间的关系返回NSComparisonResult
。
然后你会调用sortedArrayUsingSelector:
与@selector(compare:)
,它应该完成。
还有其他的方法,但是据我所知, Comparable
接口没有Cocoa-equiv。 使用sortedArrayUsingSelector:
可能是最简单的方法。
GeorgSchölly的第二个答案有一个缺失的步骤,但是它的工作正常。
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];
谢谢,它工作正常…
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
方法。 它sorting它,而不创build一个新的实例。
如果你只是sorting一个NSNumbers
数组,你可以用1个呼叫进行sorting:
[arrayToSort sortUsingSelector: @selector(compare:)];
这是因为数组中的对象( NSNumber
对象)实现比较方法。 您可以为NSString
对象做同样的事情,甚至可以为实现比较方法的自定义数据对象的数组。
以下是使用比较器块的一些示例代码。 它对字典数组进行sorting,每个字典在一个键“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; }];
上面的代码完成了为每个sorting键获取一个整数值并对它们进行比较的工作,以说明如何去做。 由于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]];
我倾向于喜欢简单的语句和大量的临时variables,因为代码更容易阅读,并且更易于debugging。 编译器优化了临时variables,所以对于所有的一行版本来说没有任何优势。
我已经创build了一个类库方法的小库,称为Linq to ObjectiveC ,使这种事情更容易。 使用带有按键select器的sorting方法,您可以按照birthDate
进行sorting,如下所示:
NSArray* sortedByBirthDate = [input sort:^id(id person) { return [person birthDate]; }]
我只是根据自定义要求进行多级分类。
//对值进行sorting
[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]; }
您可以使用以下通用方法来达到您的目的。 它应该解决你的问题。
//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];
sortingNSMutableArray
非常简单:
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];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"_strPrice" ascending:sortFlag selector:@selector(localizedStandardCompare:)] ;
使用NSComparator进行sorting
如果我们想sorting自定义对象,我们需要提供NSComparator
,它用来比较自定义对象。 该块返回一个NSComparisonResult
值来表示这两个对象的sorting。 所以为了sorting整个数组NSComparator
以下面的方式使用。
NSArray *sortedArray = [employeesArray sortedArrayUsingComparator:^NSComparisonResult(Employee *e1, Employee *e2){ return [e1.firstname compare:e2.firstname]; }];
使用NSSortDescriptor进行sorting
作为一个例子,假设我们有一个包含自定义类的实例的数组,Employee有属性firstname,lastname和age。 下面的例子说明了如何创build一个NSSortDescriptor,它可以用年龄键以升序对数组内容进行sorting。
NSSortDescriptor *ageDescriptor = [[NSSortDescriptor alloc] initWithKey:@"age" ascending:YES]; NSArray *sortDescriptors = @[ageDescriptor]; NSArray *sortedArray = [employeesArray sortedArrayUsingDescriptors:sortDescriptors];
使用自定义比较进行sorting
名称是string,并且当您对string进行sorting以呈现给用户时,您应该始终使用本地化的比较。 通常你也希望执行不区分大小写的比较。 这里有一个例子(localizedStandardCompare :),用last和first命名数组。
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/
在我的情况下,我使用“sortedArrayUsingComparator”sorting数组。 看下面的代码。
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的协议和函数式编程使得这很简单,只需要让类符合Comparable协议,实现协议所需的方法,然后使用sorting后的(by:)高阶函数创build一个sorting的数组而不需要使用顺便说一下可变数组。
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)
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(@"===============================");