我想做的事情似乎很简单,但我在网上找不到任何答案。我有 NSMutableArray
个对象,假设它们是“Person”对象。我想按NSDate
的 Person.birthDate 对 NSMutableArray
进行排序。
我认为这与这种方法有关:
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]];
您可以通过向数组添加多个键轻松地按多个键排序。也可以使用自定义比较器方法。看看the documentation。
块(闪亮!)
自 Mac OS X 10.6 和 iOS 4 起,也可以使用块进行排序:
NSArray *sortedArray;
sortedArray = [drinkDetails sortedArrayUsingComparator:^NSComparisonResult(Person *a, Person *b) {
return [a.birthDate compare:b.birthDate];
}];
表现
一般来说,-compare:
和基于块的方法会比使用 NSSortDescriptor
快很多,因为后者依赖于 KVC。 NSSortDescriptor
方法的主要优点是它提供了一种使用数据而不是代码来定义排序顺序的方法,这使得设置变得很容易,例如,用户可以通过单击标题行对 NSTableView
进行排序.
请参阅 NSMutableArray
方法 sortUsingFunction:context:
您将需要设置一个 compare 函数,该函数接受两个对象(类型为 Person
,因为您正在比较两个 Person
对象)和一个 context 参数。
这两个对象只是 Person
的实例。第三个对象是一个字符串,例如@"birthDate"。
此函数返回一个 NSComparisonResult
:如果 PersonA.birthDate
< 则返回 NSOrderedAscending
PersonB.birthDate
。如果 PersonA.birthDate
> 则返回 NSOrderedDescending
PersonB.birthDate
。最后,如果 PersonA.birthDate
== PersonB.birthDate
,它将返回 NSOrderedSame
。
这是粗略的伪代码;您将需要充实一个日期“更少”、“更多”或“等于”另一个日期的含义(例如比较 seconds-since-epoch 等):
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;
PS:这里是降序排序。
downcasting
不需要在弱变量之前进行强制转换。
return ((!obj1 && !obj2) ? NSOrderedSame : (obj1 ? NSOrderedAscending : NSOrderedDescending))
从 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]];
Georg Schölly's second answer 中缺少一个步骤,但它可以正常工作。
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"birthDate"
ascending:YES] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray;
sortedArray = [drinkDetails sortedArrayUsingDescriptors:sortDescriptors];
// 添加 's' 因为复制和粘贴时浪费了时间,并且在 sortedArrayUsingDescriptors 中没有 's' 失败
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"birthDate" ascending:YES] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray;
sortedArray = [drinkDetails sortedArrayUsingDescriptors:sortDescriptors];
谢谢,它工作正常...
您的 Person
对象需要实现一个方法,例如 compare:
,它采用另一个 Person
对象,并根据 2 个对象之间的关系返回 NSComparisonResult
。
然后您将使用 @selector(compare:)
调用 sortedArrayUsingSelector:
并且应该完成。
还有其他方法,但据我所知,Comparable
接口没有 Cocoa-equiv。使用 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
方法。它对它进行排序,而不创建新实例。
sortUsingComparator:
。
您可以根据您的目的使用以下通用方法。它应该可以解决您的问题。
//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
的数组进行排序,则可以通过 1 次调用对它们进行排序:
[arrayToSort sortUsingSelector: @selector(compare:)];
这是因为数组中的对象(NSNumber
对象)实现了 compare 方法。您可以对 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
对象实现了 compare 方法,因此可以更简单地重写它:
#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]];
我倾向于更喜欢简单的语句和大量的临时变量,因为代码更容易阅读,也更容易调试。无论如何,编译器都会优化掉临时变量,因此一体化版本没有任何优势。
您使用 NSSortDescriptor 对带有自定义对象的 NSMutableArray 进行排序
NSSortDescriptor *sortingDescriptor;
sortingDescriptor = [[NSSortDescriptor alloc] initWithKey:@"birthDate"
ascending:YES];
NSArray *sortArray = [drinkDetails sortedArrayUsingDescriptors:@[sortDescriptor]];
-(NSMutableArray*) sortArray:(NSMutableArray *)toBeSorted
{
NSArray *sortedArray;
sortedArray = [toBeSorted sortedArrayUsingComparator:^NSComparisonResult(id a, id b)
{
return [a compare:b];
}];
return [sortedArray mutableCopy];
}
我创建了一个名为 Linq to ObjectiveC 的小型类别方法库,它使这类事情变得更容易。使用带有键选择器的 sort 方法,您可以按 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
非常简单:
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 进行排序 假设我们有一个包含自定义类实例的数组,Employee 具有属性 firstname、lastname 和 age。下面的示例说明了如何创建一个 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
您必须创建 sortDescriptor,然后您可以使用 sortDescriptor 对 nsmutablearray 进行排序,如下所示。
let sortDescriptor = NSSortDescriptor(key: "birthDate", ascending: true, selector: #selector(NSString.compare(_:)))
let array = NSMutableArray(array: self.aryExist.sortedArray(using: [sortDescriptor]))
print(array)
在 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
}
}
现在让 User
有一个 Array
的 extension
。简单来说,让我们只为那些只有 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)
仅当数组的类型为 [User] || 时,扩展中的上述方法才可访问数组<用户>
像这样用于嵌套对象,
NSSortDescriptor * sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"lastRoute.to.lastname" ascending:YES selector:@selector(caseInsensitiveCompare:)];
NSMutableArray *sortedPackages = [[NSMutableArray alloc]initWithArray:[packages sortedArrayUsingDescriptors:@[sortDescriptor]]];
lastRoute 是一个对象,该对象保存 to 对象,该对象保存 lastname 字符串值。
斯威夫特版本:5.1
如果您有一个自定义结构或类并希望对它们进行任意排序,则应使用对您指定的字段进行排序的尾随闭包调用 sort()。这是一个使用对特定属性进行排序的自定义结构数组的示例:
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
}
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(@"===============================");
不定期副业成功案例分享
NSMutableArray
,我更喜欢使用方法sortUsingDescriptors
、sortUsingFunction
或sortUsingSelector
。只要数组是可变的,我通常不需要排序副本。