Wie kann ich eine NSMutableArray mit benutzerdefinierten Objekten darin sortieren?
-
03-07-2019 - |
Frage
Was ich tun möchte, scheint ziemlich einfach, aber ich kann keine Antworten im Web finden. Ich habe eine NSMutableArray
von Objekten, und lassen Sie uns sagen, dass sie ‚Person‘ Objekte sind. Ich möchte die NSMutableArray
von Person.birthDate sortieren, die ein NSDate
ist.
Ich denke, es hat etwas mit dieser Methode zu tun:
NSArray *sortedArray = [drinkDetails sortedArrayUsingSelector:@selector(???)];
In Java würde ich mein Objekt implementieren vergleichbar machen, oder verwenden Sie Collections.sort mit einem Inline-benutzerdefinierten Komparator ... wie auf der Erde Sie dies tun in Objective-C?
Lösung
Vergleichen Methode
Entweder Sie implementieren eine Vergleichs-Methode für das Objekt:
- (NSComparisonResult)compare:(Person *)otherObject {
return [self.birthDate compare:otherObject.birthDate];
}
NSArray *sortedArray = [drinkDetails sortedArrayUsingSelector:@selector(compare:)];
NSSortDescriptor (besser)
oder in der Regel noch besser:
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"birthDate"
ascending:YES];
NSArray *sortedArray = [drinkDetails sortedArrayUsingDescriptors:@[sortDescriptor]];
Sie können einfach sortieren nach mehrere Schlüssel, die von mehr als einer auf dem Array hinzufügen. benutzerdefinierte Komparator-Methoden ist ebenfalls möglich. Werfen Sie einen Blick auf der Dokumentation .
Blocks (glänzend!)
Es gibt auch die Möglichkeit, mit einem Block, da Mac OS X von Sortier- 10.6 und 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
Die -compare:
und blockbasierten Verfahren werden ziemlich viel schneller, in der Regel als die Verwendung von NSSortDescriptor
da letztere auf KVC beruht. Der Hauptvorteil der NSSortDescriptor
Methode ist, dass es eine Möglichkeit bietet, Ihre Sortierreihenfolge unter Verwendung von Daten zu definieren, anstatt Code, was es einfach macht, auf beispielsweise setzen die Dinge, so dass Benutzer eine NSTableView
durch einen Klick auf die Kopfzeile sortieren können.
Andere Tipps
Sehen Sie die NSMutableArray
Methode sortUsingFunction:context:
Sie müssen ein vergleichen Funktion, die zwei Objekte (vom Typ Person
, da Sie zwei Person
Objekte vergleichen) nimmt einzurichten und ein Kontext Parameter.
Die beiden Objekte sind nur Instanzen von Person
. Die dritte Aufgabe ist eine Zeichenfolge, z.B. @ "Birth".
Diese Funktion gibt einen NSComparisonResult
: Es gibt NSOrderedAscending
wenn PersonA.birthDate
<PersonB.birthDate
. Es wird wieder NSOrderedDescending
wenn PersonA.birthDate
> PersonB.birthDate
. Schließlich wird sie zurückkehren NSOrderedSame
wenn PersonA.birthDate
== PersonB.birthDate
.
Das ist grober Pseudo-Code; Sie müssen konkretisieren, was es bedeutet, für ein Datum „weniger“ zu sein, „mehr“ oder „gleich“ auf ein anderes Datum (wie-Sekunden seit Epoche usw. verglichen werden.):
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 kompaktere möchten, 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 beschleunigen ein wenig, wenn Sie eine Menge tun.
Ich habe das in iOS 4 einen Block verwenden. Wäre die Elemente meiner Array von id meinen Klassentyp zu werfen. In diesem Fall war es eine Klasse mit dem Namen Score mit einer Eigenschaft Punkte genannt.
Auch müssen Sie entscheiden, was ist, wenn die Elemente von Array zu tun sind nicht die richtige Art, für dieses Beispiel, das ich NSOrderedSame
gerade zurückgekehrt, aber in meinem Code ich aber eine Ausnahme.
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 ist die Sortierung in absteigender Reihenfolge
.Ab iOS 4 können Sie auch Blöcke für die Sortierung verwendet werden.
Für dieses spezielle Beispiel gehe ich davon aus, dass die Objekte im Array eine ‚Position‘ Methode, die eine NSInteger
zurück.
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: die "sortiert" Array Autoreleased werden
Ich habe versucht, alles, aber das funktionierte für mich. In einer Klasse habe ich eine andere Klasse „crimeScene
“ genannt, und will durch eine Eigenschaft von „crimeScene
“.
Das funktioniert wie ein Zauber:
NSSortDescriptor *sorter = [[NSSortDescriptor alloc] initWithKey:@"crimeScene.distance" ascending:YES];
[self.arrAnnotations sortUsingDescriptors:[NSArray arrayWithObject:sorter]];
Es gibt einen fehlenden Schritt in zweite Antwort Georg Schölly, 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 benötigen eine Methode, sagen compare:
zu implementieren, die eine andere Person
Aufgabe übernimmt, und das Rück NSComparisonResult
nach der Beziehung zwischen den zwei Objekten.
Dann würden Sie sortedArrayUsingSelector:
mit @selector(compare:)
nennen und es sollte getan werden.
Es gibt andere Möglichkeiten, aber soweit ich weiß, gibt es keinen Cocoa-equiv des Comparable
Schnittstelle. sortedArrayUsingSelector:
verwendet, wird wahrscheinlich die meisten schmerzlos Weg, es zu tun.
iOS 4 Blöcke sparen Sie:)
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 wenig Beschreibung
Für NSMutableArray
, verwenden Sie die sortUsingSelector
Methode. Er sortiert sie Platz, ohne eine neue Instanz zu schaffen.
Sie können die folgende allgemeine Methode für Ihre Zwecke 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 eine Reihe von NSNumbers
Sortierung, können Sie sie mit 1 Aufruf sortieren:
[arrayToSort sortUsingSelector: @selector(compare:)];
Das funktioniert, weil die Objekte im Array (NSNumber
Objekte), um die vergleichen Methode implementieren. Man könnte das gleiche für NSString
Objekte tun, oder auch für eine Reihe von benutzerdefinierten Datenobjekten, die einen Vergleich Methode implementieren.
Hier ist ein Beispiel-Code mit Vergleicherblöcken. Es sortiert ein Array von Wörterbüchern, wo jedes Wörterbuch eine Zahl 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 Code geht oben durch die Arbeit eines Integer-Wert für jeden Sortierschlüssel zu bekommen und zu vergleichen sie, als Beispiel dafür, wie es zu tun. Da NSNumber
Objekte einen Vergleich Methode implementieren, könnte es einfach neu geschrieben viel mehr 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 Vergleichs sogar destilliert bis zu 1 Zeile werden:
return [[obj1 objectForKey: SORT_KEY] compare: [obj2 objectForKey: SORT_KEY]];
neige ich dazu, einfache Aussagen und viele temporären Variablen bevorzugen, da der Code leichter zu lesen ist, und leichter zu debuggen. Der Compiler optimiert die temporären Variablen sowieso weg, so gibt es keinen Vorteil für die all-in-one-line-Version ist.
Ich habe eine kleine Bibliothek der Kategorie Methoden erstellt, die so genannte Linq to ObjectiveC , das macht diese Art von Sache einfacher. Unter Verwendung der sortieren Methode mit einem Schlüsselschalter Sie birthDate
sortieren können wie folgt:
NSArray* sortedByBirthDate = [input sort:^id(id person) {
return [person birthDate];
}]
ich nur Multi-Level-Sortierung erfolgen basierend auf kundenspezifischen Anforderung.
// die Werte sortieren
[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
Ich habe verwendet sortUsingFunction :: in einigen meiner Projekte:
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];
}
Sie verwenden NSSortDescriptor eine NSMutableArray mit benutzerdefinierten Objekten zu sortieren
NSSortDescriptor *sortingDescriptor;
sortingDescriptor = [[NSSortDescriptor alloc] initWithKey:@"birthDate"
ascending:YES];
NSArray *sortArray = [drinkDetails sortedArrayUsingDescriptors:@[sortDescriptor]];
Sortierung 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];
Sortieren mit NSComparator
Wenn wir benutzerdefinierte Objekte sortieren wollen, müssen wir NSComparator
zur Verfügung zu stellen, die verwendet wird, benutzerdefinierte Objekte zu vergleichen. Der Block gibt einen NSComparisonResult
Wert die Reihenfolge der beiden Objekte zu bezeichnen. Also, um ganze Reihe NSComparator
zu sortieren wird in folgenden Weise verwendet wird.
NSArray *sortedArray = [employeesArray sortedArrayUsingComparator:^NSComparisonResult(Employee *e1, Employee *e2){
return [e1.firstname compare:e2.firstname];
}];
Sortierungen Mit NSSortDescriptor
Nehmen wir an, als Beispiel, dass wir ein Array mit Instanzen einer benutzerdefinierten Klasse haben, hat Mitarbeiter Attribute Vorname, Nachname und Alter. Das folgende Beispiel zeigt, wie eine NSSortDescriptor zu schaffen, die verwendet werden können, um den Array-Inhalt aufsteigend nach Altem Schlüssel zu sortieren.
NSSortDescriptor *ageDescriptor = [[NSSortDescriptor alloc] initWithKey:@"age" ascending:YES];
NSArray *sortDescriptors = @[ageDescriptor];
NSArray *sortedArray = [employeesArray sortedArrayUsingDescriptors:sortDescriptors];
Sortieren mit dem Individual Vergleichen
Namen sind Strings, und wenn Sie Zeichenfolgen sortieren, um dem Benutzer zu präsentieren sollten Sie immer einen lokalisierten Vergleich verwenden. Oft wollen Sie auch einen Groß- und Kleinschreibung Vergleich auszuführen. Hier kommt ein Beispiel mit (localizedStandardCompare :) das Array von Namen und Vornamen zu bestellen.
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];
Für Referenz- und ausführliche Diskussion siehe auch:
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 Protokolle und funktionale Programmierung machen, dass es sehr einfach, Sie müssen nur Ihre Klasse zu dem vergleichbaren Protokoll entsprechen machen, implementieren, um die durch das Protokoll erforderlichen Methoden und verwenden Sie dann die sortierte (von:) Funktion höherer Ordnung ein sortiertes Array zu erstellen, ohne müssen wandelbar Arrays durch die Art und Weise verwendet werden.
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)
In meinem Fall verwende ich „sortedArrayUsingComparator“ ein Array zu sortieren. Schauen Sie sich den folgenden Code ein.
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 Ziel ist es,
@interface ContactListData : JsonData
@property(nonatomic,strong) NSString * contactName;
@property(nonatomic,strong) NSString * contactSurname;
@property(nonatomic,strong) NSString * contactPhoneNumber;
@property(nonatomic) BOOL isSelected;
@end
Sortieren Array In Swift
Für Swifty
Person ist eine sehr saubere Technik für global oben Ziel zu erreichen. ein Beispiel benutzerdefinierte Klasse von User
Lets haben, die einige Attribute haben.
class User: NSObject {
var id: String?
var name: String?
var email: String?
var createdDate: Date?
}
Jetzt haben wir eine Reihe, die wir auf der Basis von createdDate
entweder auf- und / oder absteigend sortiert werden müssen. So kann eine Funktion zum Datumsvergleich hinzufügen.
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
}
}
können Sie nun eine extension
von Array
für User
haben. In einfachen Worten können einige Methoden hinzufügen nur für diejenigen, nur haben die das Angebot von Array User
Objekte darin.
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 Sortierung
let sortedArray = someArray.sortUserByDate(.orderedAscending)
Verwendung für absteigende Sortierung
let sortedArray = someArray.sortUserByDate(.orderedAscending)
Verwendung für Same Auftrag
let sortedArray = someArray.sortUserByDate(.orderedSame)
Vor Methode in
extension
nur zugänglich, wenn dieArray
vom Typ[User]
||Array<User>
Sie haben SortDescriptor zu erstellen und dann können Sie die NSMutableArray sortieren, indem SortDescriptor mit wie unten.
let sortDescriptor = NSSortDescriptor(key: "birthDate", ascending: true, selector: #selector(NSString.compare(_:)))
let array = NSMutableArray(array: self.aryExist.sortedArray(using: [sortDescriptor]))
print(array)
wie folgt verwenden 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 das Objekt hält das zu widersprechen, dass die Nachnamen String-Werte zu halten Objekt.
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"_strPrice"
ascending:sortFlag selector:@selector(localizedStandardCompare:)] ;
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(@"===============================");