Was ich tun möchte, scheint ziemlich einfach zu sein, aber ich kann im Web keine Antworten finden. Ich habe eine NSMutableArray
Anzahl von Objekten und sagen wir, es handelt sich um 'Personen'-Objekte. Ich möchte das NSMutableArray
nach Person.birthDate sortieren, bei dem es sich um ein NSDate
handelt.
Ich denke, es hat etwas mit dieser Methode zu tun:
NSArray *sortedArray = [drinkDetails sortedArrayUsingSelector:@selector(???)];
In Java würde ich mein Objekt Comparable implementieren lassen oder Collections.sort mit einem benutzerdefinierten Inline-Komparator verwenden ... wie um alles in der Welt machen Sie das in Objective-C?
Entweder implementieren Sie eine Vergleichsmethode für Ihr Objekt:
- (NSComparisonResult)compare:(Person *)otherObject {
return [self.birthDate compare:otherObject.birthDate];
}
NSArray *sortedArray = [drinkDetails sortedArrayUsingSelector:@selector(compare:)];
oder meist noch besser:
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"birthDate"
ascending:YES];
NSArray *sortedArray = [drinkDetails sortedArrayUsingDescriptors:@[sortDescriptor]];
Sie können problemlos nach mehreren Schlüsseln sortieren, indem Sie dem Array mehrere hinzufügen. Die Verwendung von benutzerdefinierten Vergleichsmethoden ist ebenfalls möglich. Schauen Sie sich die Dokumentation an .
Seit Mac OS X 10.6 und iOS 4 besteht auch die Möglichkeit, mit einem Block zu sortieren:
NSArray *sortedArray;
sortedArray = [drinkDetails sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
NSDate *first = [(Person*)a birthDate];
NSDate *second = [(Person*)b birthDate];
return [first compare:second];
}];
Die -compare:
und blockbasierten Methoden sind im Allgemeinen viel schneller als die Verwendung von NSSortDescriptor
, da letztere auf KVC basieren. Der Hauptvorteil der NSSortDescriptor
-Methode besteht darin, dass Sie Ihre Sortierreihenfolge mithilfe von Daten anstelle von Code definieren können, wodurch es einfach wird, z. Richten Sie die Dinge so ein, dass Benutzer ein NSTableView
sortieren können, indem Sie auf die Kopfzeile klicken.
Siehe die NSMutableArray
-Methode sortUsingFunction:context:
Sie müssen eine Vergleichsfunktion einrichten, die zwei Objekte (vom Typ Person
) akzeptiert, da Sie zwei Person
vergleichen. Objekte) und ein Kontext Parameter.
Die beiden Objekte sind nur Instanzen von Person
. Das dritte Objekt ist eine Zeichenkette, z.B. @"Geburtsdatum".
Diese Funktion gibt ein NSComparisonResult
zurück: Sie gibt NSOrderedAscending
zurück, wenn _PersonA.birthDate
_ <_PersonB.birthDate
_. Es wird NSOrderedDescending
zurückgegeben, wenn _PersonA.birthDate
_> _PersonB.birthDate
_. Schließlich wird NSOrderedSame
zurückgegeben, wenn _PersonA.birthDate
_ == _PersonB.birthDate
_.
Dies ist ein grober Pseudocode. Sie müssen herausfinden, was es bedeutet, wenn ein Datum "weniger", "mehr" oder "gleich" mit einem anderen Datum ist (z. B. Vergleich von Sekunden seit der Epoche usw.):
_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;
}
_
Wenn Sie etwas kompakteres wollen, können Sie ternäre Operatoren verwenden:
_NSComparisonResult compare(Person *firstPerson, Person *secondPerson, void *context) {
return ([firstPerson birthDate] < [secondPerson birthDate]) ? NSOrderedAscending : ([firstPerson birthDate] > [secondPerson birthDate]) ? NSOrderedDescending : NSOrderedSame;
}
_
Inlining könnte dies vielleicht etwas beschleunigen, wenn Sie dies häufig tun.
Ich habe dies in iOS 4 mit einem Block gemacht. Musste die Elemente meines Arrays von id in meinen Klassentyp umwandeln. In diesem Fall war es eine Klasse namens Score mit einer Eigenschaft namens points.
Außerdem müssen Sie entscheiden, was zu tun ist, wenn die Elemente Ihres Arrays nicht den richtigen Typ haben. In diesem Beispiel habe ich NSOrderedSame
zurückgegeben, in meinem Code habe ich jedoch eine Ausnahme gemacht.
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: Dies wird in absteigender Reihenfolge sortiert.
Ab iOS 4 können Sie Blöcke auch zum Sortieren verwenden.
In diesem Beispiel gehe ich davon aus, dass die Objekte in Ihrem Array eine 'position'-Methode haben, die ein NSInteger
zurückgibt.
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];
Hinweis: Das "sortierte" Array wird automatisch freigegeben.
Ich habe alles versucht, aber das hat bei mir funktioniert. In einer Klasse habe ich eine andere Klasse mit dem Namen "crimeScene
" und möchte nach einer Eigenschaft von "crimeScene
" sortieren.
Das funktioniert wie ein Zauber:
NSSortDescriptor *sorter = [[NSSortDescriptor alloc] initWithKey:@"crimeScene.distance" ascending:YES];
[self.arrAnnotations sortUsingDescriptors:[NSArray arrayWithObject:sorter]];
Es fehlt ein Schritt in Georg Schöllys zweite Antwort , aber es funktioniert dann gut.
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];
Danke, es funktioniert gut ...
Ihre Person
Objekte müssen eine Methode implementieren, z. B. compare:
, die ein anderes Person
Objekt annimmt, und NSComparisonResult
gemäß der Beziehung zwischen den beiden Objekten zurückgeben.
Dann würden Sie sortedArrayUsingSelector:
mit @selector(compare:)
aufrufen und es sollte getan werden.
Es gibt andere Möglichkeiten, aber meines Wissens gibt es kein Cocoa-Equiv der Comparable
-Oberfläche. Die Verwendung von sortedArrayUsingSelector:
ist wahrscheinlich der schmerzloseste Weg, dies zu tun.
iOS 4 Blöcke werden dich retten :)
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 ein bisschen Beschreibung
Verwenden Sie für NSMutableArray
die Methode sortUsingSelector
. Es sortiert die Position, ohne eine neue Instanz zu erstellen.
Sie können die folgende generische Methode für Ihren Zweck verwenden. Es sollte Ihr Problem lösen.
//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];
Wenn Sie nur ein Array von NSNumbers
sortieren, können Sie diese mit einem Aufruf sortieren:
[arrayToSort sortUsingSelector: @selector(compare:)];
Das funktioniert, weil die Objekte im Array (NSNumber
Objekte) die Vergleichsmethode implementieren. Sie können dasselbe für NSString
-Objekte oder sogar für ein Array von benutzerdefinierten Datenobjekten tun, die eine Vergleichsmethode implementieren.
Hier ist ein Beispielcode mit Komparatorblöcken. Es sortiert eine Reihe von Wörterbüchern, wobei jedes Wörterbuch eine Nummer in einem Schlüssel "sort_key" enthält.
#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;
}];
Der obige Code durchläuft die Arbeit, einen ganzzahligen Wert für jeden Sortierschlüssel zu ermitteln und zu vergleichen, um zu veranschaulichen, wie dies getan wird. Da NSNumber
-Objekte eine Vergleichsmethode implementieren, kann diese viel einfacher umgeschrieben werden:
#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];
}];
oder der Körper des Komparators könnte sogar auf 1 Zeile herunterdestilliert werden:
return [[obj1 objectForKey: SORT_KEY] compare: [obj2 objectForKey: SORT_KEY]];
Ich bevorzuge einfache Anweisungen und viele temporäre Variablen, da der Code leichter zu lesen und leichter zu debuggen ist. Der Compiler optimiert die temporären Variablen ohnehin weg, sodass die All-in-One-Line-Version keinen Vorteil bietet.
Ich habe sortUsingFunction :: in einigen meiner Projekte verwendet:
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];
Ich habe eine kleine Bibliothek von Kategoriemethoden mit dem Namen Linq to ObjectiveC erstellt, die dies einfacher macht. Mit der sort -Methode mit einer Schlüsselauswahl können Sie nach birthDate
wie folgt sortieren:
NSArray* sortedByBirthDate = [input sort:^id(id person) {
return [person birthDate];
}]
Ich habe gerade eine mehrstufige Sortierung basierend auf benutzerdefinierten Anforderungen durchgeführt.
// sortiere die Werte
[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
-(NSMutableArray*) sortArray:(NSMutableArray *)toBeSorted
{
NSArray *sortedArray;
sortedArray = [toBeSorted sortedArrayUsingComparator:^NSComparisonResult(id a, id b)
{
return [a compare:b];
}];
return [sortedArray mutableCopy];
}
Mit NSSortDescriptor können Sie ein NSMutableArray nach benutzerdefinierten Objekten sortieren
NSSortDescriptor *sortingDescriptor;
sortingDescriptor = [[NSSortDescriptor alloc] initWithKey:@"birthDate"
ascending:YES];
NSArray *sortArray = [drinkDetails sortedArrayUsingDescriptors:@[sortDescriptor]];
Das Sortieren von NSMutableArray
ist sehr einfach:
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];
Mit den Protokollen und der funktionalen Programmierung von Swift ist es sehr einfach, Ihre Klasse an das Comparable-Protokoll anzupassen, die für das Protokoll erforderlichen Methoden zu implementieren und dann mithilfe der sortierten (nach:) Funktion höherer Ordnung ein sortiertes Array zu erstellen, ohne es verwenden zu müssen veränderbare Arrays übrigens.
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)
Mit NSComparator sortieren
Wenn wir benutzerdefinierte Objekte sortieren möchten, müssen wir NSComparator
bereitstellen, mit dem benutzerdefinierte Objekte verglichen werden. Der Block gibt einen NSComparisonResult
-Wert zurück, um die Reihenfolge der beiden Objekte anzugeben. Um also das gesamte Array zu sortieren, wird NSComparator
folgendermaßen verwendet.
NSArray *sortedArray = [employeesArray sortedArrayUsingComparator:^NSComparisonResult(Employee *e1, Employee *e2){
return [e1.firstname compare:e2.firstname];
}];
Sortiert mit NSSortDescriptor
Angenommen, wir haben ein Array mit Instanzen einer benutzerdefinierten Klasse. Employee hat die Attribute Vorname, Nachname und Alter. Das folgende Beispiel zeigt, wie ein NSSortDescriptor erstellt wird, mit dem der Array-Inhalt in aufsteigender Reihenfolge nach dem Altersschlüssel sortiert werden kann.
NSSortDescriptor *ageDescriptor = [[NSSortDescriptor alloc] initWithKey:@"age" ascending:YES];
NSArray *sortDescriptors = @[ageDescriptor];
NSArray *sortedArray = [employeesArray sortedArrayUsingDescriptors:sortDescriptors];
Mit benutzerdefinierten Vergleichen sortieren
Namen sind Zeichenfolgen. Wenn Sie Zeichenfolgen sortieren, die dem Benutzer angezeigt werden sollen, sollten Sie immer einen lokalisierten Vergleich verwenden. Oft möchten Sie auch einen Vergleich durchführen, bei dem die Groß- und Kleinschreibung nicht berücksichtigt wird. Hier kommt ein Beispiel mit (localizedStandardCompare :), um das Array nach Nachname und Vorname zu ordnen.
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];
Referenz und ausführliche Informationen finden Sie unter: 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/
In meinem Fall verwende ich sortArrayUsingComparator, um ein Array zu sortieren. Schauen Sie sich den folgenden Code an.
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];
}];
Auch mein Objekt ist,
@interface ContactListData : JsonData
@property(nonatomic,strong) NSString * contactName;
@property(nonatomic,strong) NSString * contactSurname;
@property(nonatomic,strong) NSString * contactPhoneNumber;
@property(nonatomic) BOOL isSelected;
@end
Verwenden Sie wie folgt für verschachtelte Objekte,
NSSortDescriptor * sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"lastRoute.to.lastname" ascending:YES selector:@selector(caseInsensitiveCompare:)];
NSMutableArray *sortedPackages = [[NSMutableArray alloc]initWithArray:[packages sortedArrayUsingDescriptors:@[sortDescriptor]]];
lastRoute ist ein Objekt und dieses Objekt enthält das Objekt to, das Objekt to enthält die Zeichenfolgenwerte lastname.
Für Swifty
Person unten ist eine sehr saubere Technik, um das übergeordnete Ziel für global zu erreichen. Nehmen wir eine benutzerdefinierte Beispielklasse von User
mit einigen Attributen.
class User: NSObject {
var id: String?
var name: String?
var email: String?
var createdDate: Date?
}
Jetzt haben wir ein Array, das wir auf der Basis von createdDate
entweder aufsteigend und/oder absteigend sortieren müssen. Fügen wir also eine Funktion für den Datumsvergleich hinzu.
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
}
}
Jetzt haben wir eine extension
von Array
für User
. In einfachen Worten lassen Sie uns einige Methoden nur für diejenigen Arrays hinzufügen, die nur User
Objekte enthalten.
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
}
}
Verwendung für aufsteigende Reihenfolge
let sortedArray = someArray.sortUserByDate(.orderedAscending)
Verwendung für absteigende Reihenfolge
let sortedArray = someArray.sortUserByDate(.orderedAscending)
Verwendung für die gleiche Bestellung
let sortedArray = someArray.sortUserByDate(.orderedSame)
Auf die obige Methode in
extension
kann nur zugegriffen werden, wennArray
vom Typ[User]
|| istArray<User>
Sie müssen sortDescriptor erstellen und dann können Sie das nsmutablearray mit sortDescriptor wie folgt sortieren.
let sortDescriptor = NSSortDescriptor(key: "birthDate", ascending: true, selector: #selector(NSString.compare(_:)))
let array = NSMutableArray(array: self.aryExist.sortedArray(using: [sortDescriptor]))
print(array)