Ich habe eine Reihe von Contact
Objekten:
var contacts:[Contact] = [Contact]()
Kontaktklasse:
Class Contact:NSOBject {
var firstName:String!
var lastName:String!
}
Und ich möchte dieses Array nach lastName
und nach sortieren, firstName
falls einige Kontakte das gleiche haben lastName
.
Ich kann nach einem dieser Kriterien sortieren, aber nicht nach beiden.
contacts.sortInPlace({$0.lastName < $1.lastName})
Wie könnte ich weitere Kriterien hinzufügen, um dieses Array zu sortieren?
Contact
sollte wahrscheinlich nicht erbenNSObject
, 2)Contact
sollte wahrscheinlich eine Struktur sein und 3)firstName
undlastName
sollte wahrscheinlich nicht implizit entpackte Optionen sein.Antworten:
Überlegen Sie, was "Sortieren nach mehreren Kriterien" bedeutet. Dies bedeutet, dass zwei Objekte zuerst nach einem Kriterium verglichen werden. Wenn diese Kriterien identisch sind, werden die Verbindungen durch die nächsten Kriterien usw. unterbrochen, bis Sie die gewünschte Bestellung erhalten.
Was Sie hier sehen, ist die
Sequence.sorted(by:)
Methode , die den bereitgestellten Abschluss konsultiert, um zu bestimmen, wie Elemente verglichen werden.Wenn Ihre Sortierung an vielen Stellen verwendet wird, ist es möglicherweise besser, Ihren Typ an das
Comparable
Protokoll anzupassen . Auf diese Weise können Sie eineSequence.sorted()
Methode verwenden , die Ihre Implementierung desComparable.<(_:_:)
Operators konsultiert, um zu bestimmen, wie Elemente verglichen werden. Auf diese Weise können Sie jedesSequence
vonContact
s sortieren, ohne jemals den Sortiercode duplizieren zu müssen.quelle
else
Body muss zwischen liegen,{ ... }
sonst wird der Code nicht kompiliert.sort
vs.sortInPlace
siehe hier . Aslo sieht dies unten, es ist viel mehr modularsortInPlace
ist in Swift 3 NICHT mehr verfügbar, stattdessen müssen Sie es verwendensort()
.sort()
mutiert das Array selbst. Es gibt auch eine neue Funktion namens,sorted()
die ein sortiertes Array zurückgibt==
ist keine gute Idee. Es funktioniert nur für 2 Eigenschaften. Mehr als das, und Sie beginnen, sich mit vielen zusammengesetzten booleschen Ausdrücken zu wiederholenVerwenden von Tupeln zum Vergleichen mehrerer Kriterien
Eine wirklich einfache Möglichkeit, eine Sortierung nach mehreren Kriterien durchzuführen (dh nach einem Vergleich zu sortieren und, falls gleichwertig, nach einem anderen Vergleich), ist die Verwendung von Tupeln , da die Operatoren
<
und>
für sie Überladungen haben, die lexikografische Vergleiche durchführen.Beispielsweise:
Dadurch werden zuerst die
lastName
Eigenschaften der Elemente verglichen . Wenn sie nicht gleich sind, basiert die Sortierreihenfolge auf einem<
Vergleich mit ihnen. Wenn sie sind gleich, dann wird es auf das nächste Paar von Elementen in dem Tupel zu bewegen, dh die VergleichsfirstName
Eigenschaften.Die Standardbibliothek bietet
<
und>
überlädt Tupel mit 2 bis 6 Elementen.Wenn Sie unterschiedliche Sortierreihenfolgen für unterschiedliche Eigenschaften wünschen, können Sie einfach die Elemente in den Tupeln austauschen:
Dies wird nun nach
lastName
absteigend und dannfirstName
aufsteigend sortiert .Definieren einer
sort(by:)
Überladung, die mehrere Prädikate benötigtInspiriert von der Diskussion über das Sortieren von Sammlungen mit
map
Closures und SortDescriptors besteht eine weitere Option darin, eine benutzerdefinierte Überladung vonsort(by:)
undsorted(by:)
mehrere Prädikate zu definieren, wobei jedes Prädikat der Reihe nach berücksichtigt wird, um die Reihenfolge der Elemente zu bestimmen.(Der
secondPredicate:
Parameter ist unglücklich, wird jedoch benötigt, um Mehrdeutigkeiten mit der vorhandenensort(by:)
Überlastung zu vermeiden. )Dies erlaubt uns dann zu sagen (unter Verwendung des
contacts
Arrays von früher):Obwohl die Anrufstelle nicht so präzise ist wie die Tupelvariante, erhalten Sie zusätzliche Klarheit darüber, was in welcher Reihenfolge verglichen wird.
Entsprechend
Comparable
Wenn Sie vorhaben , regelmäßig dann zu tun , diese Art von Vergleichen werden, wie @AMomchilov & @appzYourLife vorschlagen, können Sie anpassen
Contact
zuComparable
:Und jetzt fordern Sie einfach
sort()
eine aufsteigende Reihenfolge:oder
sort(by: >)
für eine absteigende Reihenfolge:Benutzerdefinierte Sortierreihenfolgen in einem verschachtelten Typ definieren
Wenn Sie andere Sortierreihenfolgen verwenden möchten, können Sie diese in einem verschachtelten Typ definieren:
und dann einfach anrufen als:
quelle
contacts.sort { ($0.lastName, $0.firstName) < ($1.lastName, $1.firstName) }
Hat geholfen. Vielen Dankcontacts.sort { ($0.lastName ?? "", $0.firstName ?? "") < ($1.lastName ?? "", $1.firstName ?? "") }
.""
Vergleich mit anderen Zeichenfolgen ausgeliefert (es steht vor nicht leeren Zeichenfolgen). Es ist irgendwie implizit, irgendwie magisch und unflexibel, wenn Sie möchten, dass dasnil
s stattdessen am Ende der Liste steht. Ich empfehle Ihnen, einen Blick auf meinenilComparator
Funktion stackoverflow.com/a/44808567/3141234Ein weiterer einfacher Ansatz zum Sortieren mit 2 Kriterien ist unten dargestellt.
Überprüfen Sie, ob das erste Feld, in diesem Fall ist es
lastName
, wenn sie von nicht gleich Art sindlastName
, wennlastName
‚s gleich sind, dann sortiert nach dem zweiten Feld, in diesem FallfirstName
.quelle
Das einzige, was die lexikografischen Sortierungen nicht wie von @Hamish beschrieben tun können, ist, verschiedene Sortierrichtungen zu handhaben, z. B. Sortieren nach dem ersten absteigenden Feld, dem nächsten aufsteigenden Feld usw.
Ich habe in Swift 3 einen Blog-Beitrag dazu erstellt und den Code einfach und lesbar gehalten.
Sie finden es hier:
http://master-method.com/index.php/2016/11/23/sort-a-sequence-ie-arrays-of-objects-by-multiple-properties-in-swift-3/Ein GitHub-Repository mit dem Code finden Sie auch hier:
https://github.com/jallauca/SortByMultipleFieldsSwift.playground
Das Wesentliche ist, wenn Sie eine Liste von Standorten haben, können Sie dies tun:
quelle
Diese Frage hat bereits viele gute Antworten, aber ich möchte auf einen Artikel verweisen - Deskriptoren in Swift sortieren . Wir haben verschiedene Möglichkeiten, die Sortierung nach mehreren Kriterien durchzuführen.
Bei Verwendung von NSSortDescriptor gibt es auf diese Weise einige Einschränkungen. Das Objekt sollte eine Klasse sein und von NSObject erben.
Hier wollen wir zum Beispiel nach Nachname, dann Vorname und schließlich nach Geburtsjahr sortieren. Und wir möchten dies ohne Berücksichtigung der Groß- und Kleinschreibung und unter Verwendung des Gebietsschemas des Benutzers tun.
Verwenden der Swift-Methode zum Sortieren mit Nachname / Vorname. Dieser Weg sollte mit beiden Klassen / Strukturen funktionieren. Wir sortieren hier jedoch nicht nach Geburtsjahr.
Schneller Weg, um NSSortDescriptor einzuleiten. Hierbei wird das Konzept verwendet, dass Funktionen ein erstklassiger Typ sind. SortDescriptor ist ein Funktionstyp, nimmt zwei Werte an und gibt einen Bool zurück. Sagen wir sortByFirstName, wir nehmen zwei Parameter ($ 0, $ 1) und vergleichen ihre Vornamen. Die Kombinationsfunktionen benötigen eine Reihe von SortDescriptors, vergleichen alle und geben Befehle.
Dies ist gut, da Sie es sowohl mit struct als auch mit class verwenden können. Sie können es sogar erweitern, um es mit nils zu vergleichen.
Es wird jedoch dringend empfohlen , den Originalartikel zu lesen . Es hat viel mehr Details und ist gut erklärt.
quelle
Ich würde empfehlen, die Tupellösung von Hamish zu verwenden, da kein zusätzlicher Code erforderlich ist.
Wenn Sie etwas möchten, das sich wie
if
Anweisungen verhält , aber die Verzweigungslogik vereinfacht, können Sie diese Lösung verwenden, mit der Sie Folgendes tun können:Hier sind die Funktionen, mit denen Sie dies tun können:
Wenn Sie es testen möchten, können Sie diesen zusätzlichen Code verwenden:
Der Hauptunterschied zu Jamies Lösung besteht darin, dass der Zugriff auf die Eigenschaften inline und nicht als statische / Instanzmethoden für die Klasse definiert wird. ZB
$0.family
stattAnimal.familyCompare
. Das Auf- und Absteigen wird durch einen Parameter anstelle eines überladenen Operators gesteuert. Jamies Lösung fügt eine Erweiterung für Array hinzu, während meine Lösung die integrierte Methodesort
/sorted
verwendet, jedoch zwei zusätzliche definiert werden muss:compare
undcomparisons
.Der Vollständigkeit halber ist hier meine Lösung im Vergleich zur Hamup-Tupellösung . Um zu demonstrieren, werde ich ein wildes Beispiel verwenden, in dem wir Personen nach
(name, address, profileViews)
Hamishs Lösung sortieren möchten. Dabei wird jeder der 6 Eigenschaftswerte genau einmal ausgewertet, bevor der Vergleich beginnt. Dies kann nicht oder nicht erwünscht sein. Angenommen, esprofileViews
handelt sich um einen teuren Netzwerkanruf, möchten wir möglicherweise einen Anruf vermeiden, esprofileViews
sei denn, dies ist unbedingt erforderlich. Meine Lösung wird es vermeiden,profileViews
bis$0.name == $1.name
und zu bewerten$0.address == $1.address
. Wenn es jedoch ausgewertet wird, wirdprofileViews
es wahrscheinlich viel öfter als einmal ausgewertet.quelle
Wie wäre es mit:
quelle
lexicographicallyPrecedes
erfordert, dass alle Typen im Array gleich sind. Zum Beispiel[String, String]
. Was OP wahrscheinlich möchte, ist das Mischen und Anpassen von Typen,[String, Int, Bool]
damit sie dies tun können[$0.first, $0.age, $0.isActive]
.Das hat für mein Array [String] in Swift 3 funktioniert und es scheint in Swift 4 in Ordnung zu sein
quelle