可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
What I want to do seems pretty simple, but I can't find any answers on the web. I have an NSMutableArray
of objects, and let's say they are 'Person' objects. I want to sort the NSMutableArray
by Person.birthDate which is an NSDate
.
I think it has something to do with this method:
NSArray *sortedArray = [drinkDetails sortedArrayUsingSelector:@selector(???)];
In Java I would make my object implement Comparable, or use Collections.sort with an inline custom comparator...how on earth do you do this in Objective-C?
回答1:
Compare method
Either you implement a compare-method for your object:
- (NSComparisonResult)compare:(Person *)otherObject { return [self.birthDate compare:otherObject.birthDate]; } NSArray *sortedArray = [drinkDetails sortedArrayUsingSelector:@selector(compare:)];
NSSortDescriptor (better)
or usually even better:
NSSortDescriptor *sortDescriptor; sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"birthDate" ascending:YES]; NSArray *sortedArray = [drinkDetails sortedArrayUsingDescriptors:@[sortDescriptor]];
You can easily sort by multiple keys by adding more than one to the array. Using custom comparator-methods is possible as well. Have a look at the documentation.
Blocks (shiny!)
There's also the possibility of sorting with a block since Mac OS X 10.6 and 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]; }];
Performance
The -compare:
and block-based methods will be quite a bit faster, in general, than using NSSortDescriptor
as the latter relies on KVC. The primary advantage of the NSSortDescriptor
method is that it provides a way to define your sort order using data, rather than code, which makes it easy to e.g. set things up so users can sort an NSTableView
by clicking on the header row.
回答2:
See the NSMutableArray
method sortUsingFunction:context:
You will need to set up a compare function which takes two objects (of type Person
, since you are comparing two Person
objects) and a context parameter.
The two objects are just instances of Person
. The third object is a string, e.g. @"birthDate".
This function returns an NSComparisonResult
: It returns NSOrderedAscending
if PersonA.birthDate
PersonB.birthDate. It will return NSOrderedDescending
if PersonA.birthDate
> PersonB.birthDate
. Finally, it will return NSOrderedSame
if PersonA.birthDate
== PersonB.birthDate
.
This is rough pseudocode; you will need to flesh out what it means for one date to be "less", "more" or "equal" to another date (such as comparing seconds-since-epoch etc.):
NSComparisonResult compare(Person *firstPerson, Person *secondPerson, void *context) { if ([firstPerson birthDate] [secondPerson birthDate]) return NSOrderedDescending; else return NSOrderedSame; }
If you want something more compact, you can use ternary operators:
NSComparisonResult compare(Person *firstPerson, Person *secondPerson, void *context) { return ([firstPerson birthDate] [secondPerson birthDate]) ? NSOrderedDescending : NSOrderedSame; }
Inlining could perhaps speed this up a little, if you do this a lot.
回答3:
I did this in iOS 4 using a block. Had to cast the elements of my array from id to my class type. In this case it was a class called Score with a property called points.
Also you need to decide what to do if the elements of your array are not the right type, for this example I just returned NSOrderedSame
, however in my code I though an 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
PS: This is sorting in descending order.
回答4:
Starting in iOS 4 you can also use blocks for sorting.
For this particular example I'm assuming that the objects in your array have a 'position' method, which returns an 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]
Note: the "sorted" array will be autoreleased.
回答5:
I tried all, but this worked for me. In a class I have another class named "crimeScene
", and want to sort by a property of "crimeScene
".
This works like a charm:
NSSortDescriptor *sorter = [[NSSortDescriptor alloc] initWithKey:@"crimeScene.distance" ascending:YES]; [self.arrAnnotations sortUsingDescriptors:[NSArray arrayWithObject:sorter]];
回答6:
There is a missing step in , but it works fine then.
NSSortDescriptor *sortDescriptor; sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"birthDate" ascending:YES] autorelease]; NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor]; NSArray *sortedArray; sortedArray = [drinkDetails sortedArrayUsingDescriptor:sortDescriptors];
回答7:
NSSortDescriptor *sortDescriptor; sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"birthDate" ascending:YES] autorelease]; NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor]; NSArray *sortedArray; sortedArray = [drinkDetails sortedArrayUsingDescriptors:sortDescriptors];
Thanks, it's working fine...
回答8:
Your Person
objects need to implement a method, say compare:
which takes another Person
object, and return NSComparisonResult
according to the relationship between the 2 objects.
Then you would call sortedArrayUsingSelector:
with @selector(compare:)
and it should be done.
There are other ways, but as far as I know there is no Cocoa-equiv of the Comparable
interface. Using sortedArrayUsingSelector:
is probably the most painless way to do it.
回答9:
iOS 4 blocks will save you :)
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; } } }] retain];
http://sokol8.blogspot.com/2011/04/sorting-nsarray-with-blocks.html a bit of description
回答10:
For NSMutableArray
, use the sortUsingSelector
method. It sorts it-place, without creating a new instance.
回答11:
If you're just sorting an array of NSNumbers
, you can sort them with 1 call:
[arrayToSort sortUsingSelector: @selector(compare:)];
That works because the objects in the array (NSNumber
objects) implement the compare method. You could do the same thing for NSString
objects, or even for an array of custom data objects that implement a compare method.
Here's some example code using comparator blocks. It sorts an array of dictionaries where each dictionary includes a number in a key "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
The code above goes through the work of getting an integer value for each sort key and comparing them, as an illustration of how to do it. Since NSNumber
objects implement a compare method, it could be rewritten much more simply:
#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]; }];
or the body of the comparator could even be distilled down to 1 line:
return [[obj1 objectForKey: SORT_KEY] compare: [obj2 objectForKey: SORT_KEY]];
I tend to prefer simple statements and lots of temporary variables because the code is easier to read, and easier to debug. The compiler optimizes away the temporary variables anyway, so there is no advantage to the all-in-one-line version.
回答12:
You can use the following generic method for your purpose. It should solve your issue.
//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];
回答13:
I have created a small library of category methods, called Linq to ObjectiveC, that makes this sort of thing more easy. Using the sort method with a key selector, you can sort by birthDate
as follows:
NSArray* sortedByBirthDate = [input sort:^id(id person) { return [person birthDate]; }]
回答14:
I just done multi level sorting based on custom requirement.
//sort the values
[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
回答15:
I've used sortUsingFunction:: in some of my projects:
int SortPlays(id a, id b, void* context) { Play* p1 = a; Play* p2 = b; if (p1.scorep2.score) return NSOrderedAscending; return NSOrderedSame; } ... [validPlays sortUsingFunction:SortPlays context:nil];
回答16:
-(NSMutableArray*) sortArray:(NSMutableArray *)toBeSorted { NSArray *sortedArray; sortedArray = [toBeSorted sortedArrayUsingComparator:^NSComparisonResult(id a, id b) { return [a compare:b]; }]; return [sortedArray mutableCopy]; }
回答17:
Sorting NSMutableArray
is very simple:
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];
回答18:
Swift's protocols and functional programming makes that very easy you just have to make your class conform to the Comparable protocol, implement the methods required by the protocol and then use the sorted(by: ) high order function to create a sorted array without need to use mutable arrays by the way.
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 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)
回答19:
Sort using NSComparator
If we want to sort custom objects we need to provide NSComparator
, which is used to compare custom objects. The block returns an NSComparisonResult
value to denote the ordering of the two objects. So in order to sort whole array NSComparator
is used in following way.
NSArray *sortedArray = [employeesArray sortedArrayUsingComparator:^NSComparisonResult(Employee *e1, Employee *e2){ return [e1.firstname compare:e2.firstname]; }];
Sorts Using NSSortDescriptor
Let’s assume, as an example, that we have an array containing instances of a custom class, Employee has attributes firstname, lastname and age. The following example illustrates how to create an NSSortDescriptor that can be used to sort the array contents in ascending order by the age key.
NSSortDescriptor *ageDescriptor = [[NSSortDescriptor alloc] initWithKey:@"age" ascending:YES]; NSArray *sortDescriptors = @[ageDescriptor]; NSArray *sortedArray = [employeesArray sortedArrayUsingDescriptors:sortDescriptors];
Sort using Custom Comparisons
Names are strings, and when you sort strings to present to the user you should always use a localized comparison. Often you also want to perform a case insensitive comparison. Here comes an example with (localizedStandardCompare:) to order the array by last and first name.
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];
For reference and detailed discussion please refer: 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/
回答20:
In my case, I use "sortedArrayUsingComparator" to sort an array. Look at the below code.
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]; }];
Also my object is,
@interface ContactListData : JsonData @property(nonatomic,strong) NSString * contactName; @property(nonatomic,strong) NSString * contactSurname; @property(nonatomic,strong) NSString * contactPhoneNumber; @property(nonatomic) BOOL isSelected; @end
回答21:
You can sort array of custom objects using quick sort. Please find below example using Swift 4
func sortArrayOfPersonInAscendingOrder() { if self.arrSortingPersons != nil, self.arrSortingPersons.count > 0 { self.arrSortingPersons.sort(by: { (person1, person2) -> Bool in return person1.birthDate
回答22:
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"_strPrice" ascending:sortFlag selector:@selector(localizedStandardCompare:)] ;
回答23:
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(@"===============================");