Frage Wie sortiere ich ein NSMutableArray mit benutzerdefinierten Objekten darin?


Was ich machen möchte, scheint ziemlich einfach zu sein, aber ich kann keine Antworten im Internet finden. ich habe ein NSMutableArray von Objekten, und sagen wir, sie sind 'Person'-Objekte. Ich möchte das sortieren NSMutableArray von Person.birthDate, das ist ein NSDate.

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 Inline-Custom-Comparator verwenden ... wie um alles in der Welt machst du das in Objective-C?


1173
2018-04-30 06:10


Ursprung


Antworten:


Vergleichen Sie die Methode

Entweder implementieren Sie eine Vergleichs-Methode für Ihr Objekt:

- (NSComparisonResult)compare:(Person *)otherObject {
    return [self.birthDate compare:otherObject.birthDate];
}

NSArray *sortedArray = [drinkDetails sortedArrayUsingSelector:@selector(compare:)];

NSSortDescriptor (besser)

oder normalerweise sogar 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. Schau es dir an die Dokumentation.

Blöcke (glänzend!)

Seit Mac OS X 10.6 und iOS 4 gibt es 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];
}];

Performance

Das -compare: und blockbasierte Methoden werden im Allgemeinen ein ganzes Stück schneller sein als die Verwendung NSSortDescriptor da letzterer auf KVC angewiesen ist. Der Hauptvorteil der NSSortDescriptor Methode ist, dass es eine Möglichkeit bietet, Ihre Sortierreihenfolge mit Daten statt Code zu definieren, der es z. Dinge einrichten, damit Benutzer eine sortieren können NSTableView indem Sie auf die Kopfzeile klicken.


2215
2018-04-30 06:24



Siehe die NSMutableArray Methode sortUsingFunction:context: 

Sie müssen ein einrichten vergleichen Funktion, die zwei Objekte (vom Typ Person, da du zwei vergleichst Person Objekte) und a Kontext Parameter.

Die zwei Objekte sind nur Instanzen von Person. Das dritte Objekt ist eine Zeichenfolge, z. @"Geburtsdatum".

Diese Funktion gibt ein NSComparisonResult: Es kehrt zurück NSOrderedAscending ob PersonA.birthDate < PersonB.birthDate. Es wird zurückkehren NSOrderedDescending ob PersonA.birthDate > PersonB.birthDate. Schließlich wird es zurückkehren NSOrderedSame ob PersonA.birthDate == PersonB.birthDate.

Das ist grober Pseudocode; Sie müssen angeben, was es bedeutet, dass ein Datum "weniger", "mehr" oder "gleich" zu 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 kompakter 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 das vielleicht etwas beschleunigen, wenn Sie das oft machen.


103
2018-03-24 22:15



Ich habe das in iOS 4 mit einem Block gemacht. Musste die Elemente meines Arrays von ID zu meinem 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 bin ich gerade zurückgekehrt NSOrderedSameIn meinem Code habe ich allerdings 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 ist in absteigender Reihenfolge sortiert.


58
2018-03-16 18:46



Ab iOS 4 können Sie auch Blöcke zum Sortieren verwenden.

Für dieses spezielle Beispiel gehe ich davon aus, dass die Objekte in Ihrem Array eine "position" -Methode haben, die ein 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];

Hinweis: Das "sortierte" Array wird automatisch freigegeben.


28
2017-11-22 10:51



Ich habe alles versucht, aber das hat für mich funktioniert. In einer Klasse habe ich eine andere Klasse namens "crimeScene", und möchte nach einer Eigenschaft von"crimeScene".

Das funktioniert wie ein Zauber:

NSSortDescriptor *sorter = [[NSSortDescriptor alloc] initWithKey:@"crimeScene.distance" ascending:YES];
[self.arrAnnotations sortUsingDescriptors:[NSArray arrayWithObject:sorter]];

24
2018-05-06 16:25



Es gibt einen fehlenden Schritt 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];

19
2018-03-30 05:54



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 ...


18
2018-04-30 06:20



Ihre Person Objekte müssen eine Methode implementieren, sagen wir compare: was einen anderen braucht Person Objekt und zurück NSComparisonResult entsprechend der Beziehung zwischen den 2 Objekten.

Dann würdest du anrufen sortedArrayUsingSelector: mit @selector(compare:) und es sollte getan werden.

Es gibt andere Wege, aber soweit ich weiß, gibt es keinen Kakao-Äquivalenz der Comparable Schnittstelle. Verwenden sortedArrayUsingSelector: ist wahrscheinlich der schmerzloseste Weg, es zu tun.


16
2017-11-09 13:47



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


8
2017-12-06 07:51



Zum NSMutableArray, benutze die sortUsingSelector Methode. Es sortiert den Ort, ohne eine neue Instanz zu erstellen.


7
2018-04-16 09:17



Wenn Sie nur ein Array von NSNumbersSie können sie mit 1 Anruf sortieren:

[arrayToSort sortUsingSelector: @selector(compare:)];

Das funktioniert, weil die Objekte im Array (NSNumber Objekte) implementieren die Vergleichsmethode. Sie könnten das Gleiche tun NSString Objekte oder sogar für ein Array von benutzerdefinierten Datenobjekten, die eine Vergleichsmethode implementieren.

Hier ist ein Beispielcode, der Komparatorblöcke verwendet. Es sortiert ein Array 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 Integer-Wert für jeden Sortierschlüssel zu erhalten und diese zu vergleichen, um zu verdeutlichen, wie es gemacht wird. Schon seit NSNumber Objekte implementieren eine Vergleichsmethode, sie könnte 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 bis auf 1 Linie destilliert werden:

  return [[obj1 objectForKey: SORT_KEY] compare: [obj2 objectForKey: SORT_KEY]];

Ich bevorzuge einfache Anweisungen und viele temporäre Variablen, weil der Code leichter zu lesen und einfacher zu debuggen ist. Der Compiler optimiert die temporären Variablen trotzdem, sodass die All-in-One-Line-Version keinen Vorteil bietet.


5
2018-03-06 08:29