NSArray sortieren - Zahlen und Sonderzeichen nach Buchstaben

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • NSArray sortieren - Zahlen und Sonderzeichen nach Buchstaben

    Liebe Community,

    ich versuche schon einige Zeit ein NSArray zu sortieren, bekomme es aber nicht ganz so hin wie ich es gerne hätte.

    Das NSArray beinhaltet die Buchstaben von A bis Z und das Rautezeichen (#). Die Sortierung erfolgt in folgender Methode:

    Quellcode

    1. - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
    2. {
    3. return [self.contacts.allKeys sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
    4. return [obj1 compare:obj2 options:NSDiacriticInsensitiveSearch|NSNumericSearch];
    5. }];
    6. }

    Das Problem ist nun, dass das Rautezeichen vor dem A und nicht nach dem Z kommt. Ich habe auch schon alle NSStringCompareOptions durchprobiert, aber es ändert sich nichts. Weiß jemand wie es trotzdem geht, mit so wenig Code wie möglich?

    Mit freundlichen Grüßen

    TheFuriousLion
  • EDIT: Eine einfache Lösung gibt es wahrscheinlich nicht. Ich würde eine Tabelle anlegen, in der zu jedem Zeichen sein Sortierschlüssel steht. Der Vergleich geht dann zeichenweise durch die Zeichenketten und vergleicht jeweils die Sortierschlüssel miteinander, ungefähr so:

    Quellcode

    1. for(NSInteger i = 0; i < ...; ++i) {
    2. unichar theLeft = [inLeft characterAtIndex:i];
    3. unichar theRight = [inRight characterAtIndex:i];
    4. int theResult = kSortKeys[theLeft] - kSortKeys[theRight];
    5. if(theResult != 0) {
    6. return theResult < 0 ? NSOrderedAscending : NSOrderedDescending
    7. }
    8. return ...;
    9. }

    Da fehlt natürlich noch einiges ;)
    „Meine Komplikation hatte eine Komplikation.“

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von macmoonshine ()

  • Wenn ich das mal ernst nehme, was da steht, dann musst du in der Methode einfach nur @[ @"A", @"B", …, @"Y", @"Z", @"‘"] zurückgeben.

    Ich mutmaße aber mal, dass das Array Zeichenfolgen beinhaltet, die aus den Buchstaben und dem Rautezeichen (Wo sind die Zahlen geblieben?) gebildet werden. Da stellt sich bei mir schon gleich die nächste Frage: Taucht das Rautezeichen nur am Anfang des Strings auf? Und: Wieso so wenig Code wie möglich?
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Wieso sollte die Frage nicht ernst gemeint sein?

    Ich habe einen UITableView der Strings enthält. Und dieser soll alphabetisch in Sections geordnet sein. Nun möchte ich alle Strings die entweder mit Zahlen oder Sonderzeichen beginnen in die #-Section geben. Ich habe es mit einem NSDictionary gemacht. Die Keys sind NSStrings von A bis Z und eine Raute. Zu jedem Key gibt es ein NSMutableArray, welches die Einträge für den UITableView pro Section beinhaltet.

    Es funktioniert nun alles wie geplant, nur dass die #-Section vor der A-Section kommt. Ich möchte aber unbedingt, dass die #-Section nach der Z-Section kommt, wie beispielsweise in der iOS Kontakte App.

    Ich hole mir das NSArray für die IndexTitles des UITableViews mit allKeys vom NSDictionary, aber das NSArray, das ich zurückbekomme, hat nicht die richtige Reihenfolge, d. h. ich muss dieses Array vorher sortieren, was wieder zu meinem Problem führt, dass das #-Zeichen am Index 0 ist.
  • Ganz anderer Ansatz: Sortiere das Array normal und verschiebe nachher das erste Element mit dem Sonderzeichen nach hinten. Zumal Du doch bestimmt nicht nur den Index der Tableview so sortieren möchtest, sondern auch die Arrays der einzelnen Einträge einer Sektion...

    Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.
  • MyMattes schrieb:

    Ganz anderer Ansatz: Sortiere das Array normal und verschiebe nachher das erste Element mit dem Sonderzeichen nach hinten. Zumal Du doch bestimmt nicht nur den Index der Tableview so sortieren möchtest, sondern auch die Arrays der einzelnen Einträge einer Sektion...

    Mattes


    genau. und das ein einziges mal nachdem man die daten zusammenhat. nicht jedesmal in den datasource-methoden die ja öfters aufgerufen werden.
  • Danke für alle Ratschläge bisher. Ich hab jetzt nochmals gründlich darüber nachgedacht und einen meiner Meinung nach super Ansatz gefunden.

    Ich speichere den bereits richtig sortierten NSArray in einer readonly Property. Diese übergebe ich dann in der Methode sectionIndexTitlesForTableView:
    Meine Einträge speichere ich weiterhin im NSDictionary. Vorher hatte ich aber ein weiteres NSMutableArray für die anzuzeigenden SectionIndexTitles, da ich nicht wollte, dass die SectionHeaders für leere Sections angezeigt werden. Ich habe das jetzt aber so gemacht, dass ich einfach nil zurückgebe, sollte ein NSMutableArray zu einem Key im NSDictionary leer sein. Funktioniert bestens und die Reihenfolge der Sections ist jetzt die vom SectionIndexTitle Array. Dieses Problem ist also gelöst.

    Aber noch nicht ganz. Das eigentliche Problem war ja das Sortieren des Arrays. Wie schon beschrieben, möchte ich, dass A, B und C vor 1, 2 und 3 kommt. Und das geht nur mit einem eigenen Comparator Block?
  • Eine kleine Frage zu den Selectoren, weil es gerade passt. Wenn ich beispielsweise @selector(compare:options:) habe, wie kann ich den Parameter für options setzen?

    Quellcode

    1. [array sortUsingSelector:@selector(compare:options:)];

    Alles klar, also selbst sortieren. Ich werde das morgen versuchen. Habt ihr vielleicht noch weitere Ansätze zum selbst sortieren? macmoonshine, ich verstehe leider nicht wie du das mit der Sortiertabelle meinst.

    Nachtrag:
    Eine Idee wäre wenn es nicht zwei Buchstaben sind NSOrderedDescending zurückzugeben. Aber ich weiß nicht so recht wie ich das anstellen soll.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von TheFuriousLion () aus folgendem Grund: Nachtrag hinzugefügt.

  • Michael hat Recht: sortUsingSelector akzeptiert nur Selektoren mit einem Parameter. Aber Du kannst den Selector in einen NSComparator stecken und dann per sortUsingComparator: verwenden. Der Comparator kann dann den Selector mit beliebigen Argumenten aufrufen.

    Ist aber auch egal, ich würd's so nicht machen. Lass' die eingebauten Frameworks so viel Arbeit wie möglich übernehmen - in die Stringvergleiche ist seitens Apple schon eine Menge Hirnschmalz geflossen. Das willst Du bestimmt nicht nochmal machen. Ich würde es so ähnlich machen wie MyMatthes und Gritsch es vorgeschlagen haben:

    1. Die zu sortierende Collection nach Deinen Kriterien in verschiedene Untercollections zerteilen (z.B. Buchstabe oder Sonderzeichen am Anfang)
    2. Die Untercollections mit den eingebauten Mitteln sortieren lassen
    3. Die sortierten Untercollections in der gewünschten Reihenfolge wieder zusammenfügen

    So kannst Du selbst die Grobsortierung steuern und musst Dich trotzdem nicht mit den Details der inneren Sortierung herumschlagen.
    Multigrad - 360°-Produktfotografie für den Mac
  • Also ich habe das jetzt einmal so probiert, funktioniert aber nicht. Hat jemand vielleicht einen Tipp?

    Quellcode

    1. NSArray *array = [NSArray arrayWithObjects:@"A",@"1",@"B",@"2",@"#", @"b", @"a", nil];
    2. array = [array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
    3. unichar c1 = (unichar)[obj1 substringToIndex:1];
    4. unichar c2 = (unichar)[obj2 substringToIndex:1];
    5. NSCharacterSet *characterSet = [NSCharacterSet symbolCharacterSet];
    6. NSCharacterSet *decimalDigitCharacterSet = [NSCharacterSet decimalDigitCharacterSet];
    7. NSCharacterSet *letterCharacterSet = [NSCharacterSet capitalizedLetterCharacterSet];
    8. NSComparisonResult result;
    9. if ([characterSet characterIsMember:c2]) {
    10. if ([characterSet characterIsMember:c1]) {
    11. result = [obj1 localizedCaseInsensitiveCompare:obj2];
    12. } else {
    13. result = NSOrderedDescending;
    14. }
    15. } else if ([decimalDigitCharacterSet characterIsMember:c2]) {
    16. if ([decimalDigitCharacterSet characterIsMember:c1]) {
    17. result = [obj1 localizedCaseInsensitiveCompare:obj2];
    18. } else {
    19. result = NSOrderedAscending;
    20. }
    21. } else {
    22. if ([letterCharacterSet characterIsMember:c1]) {
    23. result = [obj1 localizedCaseInsensitiveCompare:obj2];
    24. } else {
    25. result = NSOrderedAscending;
    26. }
    27. }
    28. return result;
    29. }];
    Alles anzeigen
  • Ach ja, stimmt. Ich bekomme eine Warnung wenn ich nicht caste, was ja auch klar ist, aber ich hab die nicht gesehen, weil ich schon vorher gecastet habe. Ich bin das von C# so gewöhnt.
    Ich habe den Code nun folgendermaßen angepasst, es ändert sich aber nichts an der Reihenfolge.

    Quellcode

    1. NSArray *array = [NSArray arrayWithObjects:@"A",@"1",@"B",@"2",@"#", @"b", @"a", nil];
    2. array = [array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
    3. unichar c1 = [obj1 characterAtIndex:0];
    4. unichar c2 = [obj2 characterAtIndex:0];
    5. NSCharacterSet *symbolCharacterSet = [NSCharacterSet symbolCharacterSet];
    6. NSCharacterSet *decimalDigitCharacterSet = [NSCharacterSet decimalDigitCharacterSet];
    7. NSCharacterSet *letterCharacterSet = [NSCharacterSet capitalizedLetterCharacterSet];
    8. NSComparisonResult result = NSOrderedSame;
    9. if ([symbolCharacterSet characterIsMember:c2]) {
    10. if ([symbolCharacterSet characterIsMember:c1]) {
    11. result = [obj1 localizedCaseInsensitiveCompare:obj2];
    12. } else {
    13. result = NSOrderedDescending;
    14. }
    15. } else if ([decimalDigitCharacterSet characterIsMember:c2]) {
    16. if ([decimalDigitCharacterSet characterIsMember:c1]) {
    17. result = [obj1 localizedCaseInsensitiveCompare:obj2];
    18. } else {
    19. if ([symbolCharacterSet characterIsMember:c1]) {
    20. result = NSOrderedAscending;
    21. } else {
    22. result = NSOrderedDescending;
    23. }
    24. }
    25. } else if([letterCharacterSet characterIsMember:c2]) {
    26. if ([letterCharacterSet characterIsMember:c1]) {
    27. result = [obj1 localizedCaseInsensitiveCompare:obj2];
    28. } else {
    29. result = NSOrderedAscending;
    30. }
    31. }
    32. return result;
    33. }];
    Alles anzeigen

    Meine Überlegung dazu:
    Ich überprüfe ob c2 ein Symbol ist. Ist c1 auch ein Symbol, vergleiche ich die zwei. Ist c1 aber ein Buchstabe oder eine Zahl, dann gebe ich NSOrderedDescending zurück, da Sonderzeichen nach Buchstaben und Zahlen kommen sollen.

    Trifft nichts davon zu, überprüfe ich ob c2 eine Zahl ist. Ist c1 auch eine Zahl, vergleiche ich die zwei. Ist c1 ein Buchstabe, gebe ich NSOrderedDescending zurück, ist es ein Sonderzeichen NSOrderedAscending.

    Ist auch das nicht der Fall, überprüfe ich, ob c2 ein Buchstabe ist. Ist auch c1 ein Buchstabe, vergleiche ich wieder. Ansonsten gebe ich NSOrderedAscending zurück,