Schnell - Sortieren Sie ein Array von Objekten mit mehreren Kriterien

90

Ich habe eine Reihe von ContactObjekten:

var contacts:[Contact] = [Contact]()

Kontaktklasse:

Class Contact:NSOBject {
    var firstName:String!
    var lastName:String!
}

Und ich möchte dieses Array nach lastNameund nach sortieren, firstNamefalls 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?

sbkl
quelle
2
Mach es genauso wie du es gerade gesagt hast! Ihr Code in geschweiften Klammern sollte lauten: "Wenn die Nachnamen identisch sind, sortieren Sie nach Vornamen, andernfalls nach Nachnamen."
Matt
4
Ich sehe hier ein paar Code-Gerüche: 1) Contactsollte wahrscheinlich nicht erben NSObject, 2) Contactsollte wahrscheinlich eine Struktur sein und 3) firstNameund lastNamesollte wahrscheinlich nicht implizit entpackte Optionen sein.
Alexander - Reinstate Monica
3
@AMomchilov Es gibt keinen Grund vorzuschlagen, dass Contact eine Struktur sein sollte, da Sie nicht wissen, ob der Rest seines Codes bei der Verwendung von Instanzen bereits auf Referenzsemantik beruht.
Patrick Goley
3
@AMomchilov "Wahrscheinlich" ist irreführend, weil Sie genau nichts über den Rest der Codebasis wissen. Wenn es in eine Struktur geändert wird, werden beim Mutieren der Variablen alle plötzlichen Kopien generiert, anstatt die vorliegende Instanz zu ändern. Dies ist eine drastische Änderung des Verhaltens, und dies würde "wahrscheinlich" zu Fehlern führen, da es unwahrscheinlich ist, dass sowohl für die Referenz- als auch für die Wertesemantik alles ordnungsgemäß codiert wurde.
Patrick Goley
1
@AMomchilov Habe noch keinen Grund gehört, warum es wahrscheinlich eine Struktur sein sollte. Ich glaube nicht, dass OP Vorschläge begrüßen würde, die die Semantik des Restes seines Programms ändern, insbesondere wenn es nicht einmal notwendig war, das vorliegende Problem zu lösen. Ich wusste nicht, dass Compiler-Regeln für manche legal sind ... vielleicht bin ich auf der falschen Website
Patrick Goley

Antworten:

117

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

let sortedContacts = contacts.sort {
    if $0.lastName != $1.lastName { // first, compare by last names
        return $0.lastName < $1.lastName
    }
    /*  last names are the same, break ties by foo
    else if $0.foo != $1.foo {
        return $0.foo < $1.foo
    }
    ... repeat for all other fields in the sorting
    */
    else { // All other fields are tied, break ties by last name
        return $0.firstName < $1.firstName
    }
}

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 eine Sequence.sorted()Methode verwenden , die Ihre Implementierung des Comparable.<(_:_:)Operators konsultiert, um zu bestimmen, wie Elemente verglichen werden. Auf diese Weise können Sie jedes Sequencevon Contacts sortieren, ohne jemals den Sortiercode duplizieren zu müssen.

Alexander - Monica wieder einsetzen
quelle
2
Der elseBody muss zwischen liegen, { ... }sonst wird der Code nicht kompiliert.
Luca Angeletti
Verstanden. Ich habe versucht, es zu implementieren, konnte aber die Syntax nicht richtig verstehen. Vielen Dank.
sbkl
für sortvs. sortInPlacesiehe hier . Aslo sieht dies unten, es ist viel mehr modular
Honig
sortInPlaceist in Swift 3 NICHT mehr verfügbar, stattdessen müssen Sie es verwenden sort(). sort()mutiert das Array selbst. Es gibt auch eine neue Funktion namens, sorted()die ein sortiertes Array zurückgibt
Honey
2
@AthanasiusOfAlex Verwenden ==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 wiederholen
Alexander - Reinstate Monica
116

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

/// Returns a Boolean value indicating whether the first tuple is ordered
/// before the second in a lexicographical ordering.
///
/// Given two tuples `(a1, a2, ..., aN)` and `(b1, b2, ..., bN)`, the first
/// tuple is before the second tuple if and only if
/// `a1 < b1` or (`a1 == b1` and
/// `(a2, ..., aN) < (b2, ..., bN)`).
public func < <A : Comparable, B : Comparable>(lhs: (A, B), rhs: (A, B)) -> Bool

Beispielsweise:

struct Contact {
  var firstName: String
  var lastName: String
}

var contacts = [
  Contact(firstName: "Leonard", lastName: "Charleson"),
  Contact(firstName: "Michael", lastName: "Webb"),
  Contact(firstName: "Charles", lastName: "Alexson"),
  Contact(firstName: "Michael", lastName: "Elexson"),
  Contact(firstName: "Alex", lastName: "Elexson"),
]

contacts.sort {
  ($0.lastName, $0.firstName) <
    ($1.lastName, $1.firstName)
}

print(contacts)

// [
//   Contact(firstName: "Charles", lastName: "Alexson"),
//   Contact(firstName: "Leonard", lastName: "Charleson"),
//   Contact(firstName: "Alex", lastName: "Elexson"),
//   Contact(firstName: "Michael", lastName: "Elexson"),
//   Contact(firstName: "Michael", lastName: "Webb")
// ]

Dadurch werden zuerst die lastNameEigenschaften 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 Vergleichs firstNameEigenschaften.

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:

contacts.sort {
  ($1.lastName, $0.firstName) <
    ($0.lastName, $1.firstName)
}

// [
//   Contact(firstName: "Michael", lastName: "Webb")
//   Contact(firstName: "Alex", lastName: "Elexson"),
//   Contact(firstName: "Michael", lastName: "Elexson"),
//   Contact(firstName: "Leonard", lastName: "Charleson"),
//   Contact(firstName: "Charles", lastName: "Alexson"),
// ]

Dies wird nun nach lastNameabsteigend und dann firstNameaufsteigend sortiert .


Definieren einer sort(by:)Überladung, die mehrere Prädikate benötigt

Inspiriert von der Diskussion über das Sortieren von Sammlungen mit mapClosures und SortDescriptors besteht eine weitere Option darin, eine benutzerdefinierte Überladung von sort(by:)und sorted(by:)mehrere Prädikate zu definieren, wobei jedes Prädikat der Reihe nach berücksichtigt wird, um die Reihenfolge der Elemente zu bestimmen.

extension MutableCollection where Self : RandomAccessCollection {
  mutating func sort(
    by firstPredicate: (Element, Element) -> Bool,
    _ secondPredicate: (Element, Element) -> Bool,
    _ otherPredicates: ((Element, Element) -> Bool)...
  ) {
    sort(by:) { lhs, rhs in
      if firstPredicate(lhs, rhs) { return true }
      if firstPredicate(rhs, lhs) { return false }
      if secondPredicate(lhs, rhs) { return true }
      if secondPredicate(rhs, lhs) { return false }
      for predicate in otherPredicates {
        if predicate(lhs, rhs) { return true }
        if predicate(rhs, lhs) { return false }
      }
      return false
    }
  }
}

extension Sequence {
  mutating func sorted(
    by firstPredicate: (Element, Element) -> Bool,
    _ secondPredicate: (Element, Element) -> Bool,
    _ otherPredicates: ((Element, Element) -> Bool)...
  ) -> [Element] {
    return sorted(by:) { lhs, rhs in
      if firstPredicate(lhs, rhs) { return true }
      if firstPredicate(rhs, lhs) { return false }
      if secondPredicate(lhs, rhs) { return true }
      if secondPredicate(rhs, lhs) { return false }
      for predicate in otherPredicates {
        if predicate(lhs, rhs) { return true }
        if predicate(rhs, lhs) { return false }
      }
      return false
    }
  }
}

(Der secondPredicate:Parameter ist unglücklich, wird jedoch benötigt, um Mehrdeutigkeiten mit der vorhandenen sort(by:)Überlastung zu vermeiden. )

Dies erlaubt uns dann zu sagen (unter Verwendung des contactsArrays von früher):

contacts.sort(by:
  { $0.lastName > $1.lastName },  // first sort by lastName descending
  { $0.firstName < $1.firstName } // ... then firstName ascending
  // ...
)

print(contacts)

// [
//   Contact(firstName: "Michael", lastName: "Webb")
//   Contact(firstName: "Alex", lastName: "Elexson"),
//   Contact(firstName: "Michael", lastName: "Elexson"),
//   Contact(firstName: "Leonard", lastName: "Charleson"),
//   Contact(firstName: "Charles", lastName: "Alexson"),
// ]

// or with sorted(by:)...
let sortedContacts = contacts.sorted(by:
  { $0.lastName > $1.lastName },  // first sort by lastName descending
  { $0.firstName < $1.firstName } // ... then firstName ascending
  // ...
)

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 Contactzu Comparable:

extension Contact : Comparable {
  static func == (lhs: Contact, rhs: Contact) -> Bool {
    return (lhs.firstName, lhs.lastName) ==
             (rhs.firstName, rhs.lastName)
  }

  static func < (lhs: Contact, rhs: Contact) -> Bool {
    return (lhs.lastName, lhs.firstName) <
             (rhs.lastName, rhs.firstName)
  }
}

Und jetzt fordern Sie einfach sort()eine aufsteigende Reihenfolge:

contacts.sort()

oder sort(by: >)für eine absteigende Reihenfolge:

contacts.sort(by: >)

Benutzerdefinierte Sortierreihenfolgen in einem verschachtelten Typ definieren

Wenn Sie andere Sortierreihenfolgen verwenden möchten, können Sie diese in einem verschachtelten Typ definieren:

extension Contact {
  enum Comparison {
    static let firstLastAscending: (Contact, Contact) -> Bool = {
      return ($0.firstName, $0.lastName) <
               ($1.firstName, $1.lastName)
    }
  }
}

und dann einfach anrufen als:

contacts.sort(by: Contact.Comparison.firstLastAscending)
Hamish
quelle
contacts.sort { ($0.lastName, $0.firstName) < ($1.lastName, $1.firstName) } Hat geholfen. Vielen Dank
Prabhakar Kasi
Wenn die zu sortierenden Eigenschaften wie ich optional sind, können Sie Folgendes tun : contacts.sort { ($0.lastName ?? "", $0.firstName ?? "") < ($1.lastName ?? "", $1.firstName ?? "") }.
BobCowe
Holly Molly! So einfach und doch so effizient ... warum habe ich noch nie davon gehört?! Vielen Dank!
Ethenyl
@BobCowe Damit sind Sie dem ""Vergleich mit anderen Zeichenfolgen ausgeliefert (es steht vor nicht leeren Zeichenfolgen). Es ist irgendwie implizit, irgendwie magisch und unflexibel, wenn Sie möchten, dass das nils stattdessen am Ende der Liste steht. Ich empfehle Ihnen, einen Blick auf meine nilComparatorFunktion stackoverflow.com/a/44808567/3141234
Alexander - Reinstate Monica
18

Ein 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 sind lastName, wenn lastName‚s gleich sind, dann sortiert nach dem zweiten Feld, in diesem Fall firstName.

contacts.sort { $0.lastName == $1.lastName ? $0.firstName < $1.firstName : $0.lastName < $1.lastName  }
oyalhi
quelle
Dies bietet mehr Flexibilität als Tupel.
Babac
5

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:

struct Location {
    var city: String
    var county: String
    var state: String
}

var locations: [Location] {
    return [
        Location(city: "Dania Beach", county: "Broward", state: "Florida"),
        Location(city: "Fort Lauderdale", county: "Broward", state: "Florida"),
        Location(city: "Hallandale Beach", county: "Broward", state: "Florida"),
        Location(city: "Delray Beach", county: "Palm Beach", state: "Florida"),
        Location(city: "West Palm Beach", county: "Palm Beach", state: "Florida"),
        Location(city: "Savannah", county: "Chatham", state: "Georgia"),
        Location(city: "Richmond Hill", county: "Bryan", state: "Georgia"),
        Location(city: "St. Marys", county: "Camden", state: "Georgia"),
        Location(city: "Kingsland", county: "Camden", state: "Georgia"),
    ]
}

let sortedLocations =
    locations
        .sorted(by:
            ComparisonResult.flip <<< Location.stateCompare,
            Location.countyCompare,
            Location.cityCompare
        )
Jaime Allauca
quelle
1
"Das einzige, was die lexikografischen Sortierungen nicht wie von @Hamish beschrieben tun können, ist, mit unterschiedlichen Sortierrichtungen umzugehen" - ja, sie können einfach die Elemente in den Tupeln austauschen;)
Hamish
Ich finde das eine interessante theoretische Übung, aber viel komplizierter als die Antwort von @ Hamish. Weniger Code ist meiner Meinung nach besserer Code.
Manuel
5

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.

  1. Bei Verwendung von NSSortDescriptor gibt es auf diese Weise einige Einschränkungen. Das Objekt sollte eine Klasse sein und von NSObject erben.

    class Person: NSObject {
        var first: String
        var last: String
        var yearOfBirth: Int
        init(first: String, last: String, yearOfBirth: Int) {
            self.first = first
            self.last = last
            self.yearOfBirth = yearOfBirth
        }
    
        override var description: String {
            get {
                return "\(self.last) \(self.first) (\(self.yearOfBirth))"
            }
        }
    }
    
    let people = [
        Person(first: "Jo", last: "Smith", yearOfBirth: 1970),
        Person(first: "Joe", last: "Smith", yearOfBirth: 1970),
        Person(first: "Joe", last: "Smyth", yearOfBirth: 1970),
        Person(first: "Joanne", last: "smith", yearOfBirth: 1985),
        Person(first: "Joanne", last: "smith", yearOfBirth: 1970),
        Person(first: "Robert", last: "Jones", yearOfBirth: 1970),
    ]

    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.

    let lastDescriptor = NSSortDescriptor(key: "last", ascending: true,
      selector: #selector(NSString.localizedCaseInsensitiveCompare(_:)))
    let firstDescriptor = NSSortDescriptor(key: "first", ascending: true, 
      selector: #selector(NSString.localizedCaseInsensitiveCompare(_:)))
    let yearDescriptor = NSSortDescriptor(key: "yearOfBirth", ascending: true)
    
    
    
    (people as NSArray).sortedArray(using: [lastDescriptor, firstDescriptor, yearDescriptor]) 
    // [Robert Jones (1970), Jo Smith (1970), Joanne smith (1970), Joanne smith (1985), Joe Smith (1970), Joe Smyth (1970)]
  2. Verwenden der Swift-Methode zum Sortieren mit Nachname / Vorname. Dieser Weg sollte mit beiden Klassen / Strukturen funktionieren. Wir sortieren hier jedoch nicht nach Geburtsjahr.

    let sortedPeople = people.sorted { p0, p1 in
        let left =  [p0.last, p0.first]
        let right = [p1.last, p1.first]
    
        return left.lexicographicallyPrecedes(right) {
            $0.localizedCaseInsensitiveCompare($1) == .orderedAscending
        }
    }
    sortedPeople // [Robert Jones (1970), Jo Smith (1970), Joanne smith (1985), Joanne smith (1970), Joe Smith (1970), Joe Smyth (1970)]
  3. 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.

    typealias SortDescriptor<Value> = (Value, Value) -> Bool
    
    let sortByFirstName: SortDescriptor<Person> = {
        $0.first.localizedCaseInsensitiveCompare($1.first) == .orderedAscending
    }
    let sortByYear: SortDescriptor<Person> = { $0.yearOfBirth < $1.yearOfBirth }
    let sortByLastName: SortDescriptor<Person> = {
        $0.last.localizedCaseInsensitiveCompare($1.last) == .orderedAscending
    }
    
    func combine<Value>
        (sortDescriptors: [SortDescriptor<Value>]) -> SortDescriptor<Value> {
        return { lhs, rhs in
            for isOrderedBefore in sortDescriptors {
                if isOrderedBefore(lhs,rhs) { return true }
                if isOrderedBefore(rhs,lhs) { return false }
            }
            return false
        }
    }
    
    let combined: SortDescriptor<Person> = combine(
        sortDescriptors: [sortByLastName,sortByFirstName,sortByYear]
    )
    people.sorted(by: combined)
    // [Robert Jones (1970), Jo Smith (1970), Joanne smith (1970), Joanne smith (1985), Joe Smith (1970), Joe Smyth (1970)]

    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.

XueYu
quelle
2

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 ifAnweisungen verhält , aber die Verzweigungslogik vereinfacht, können Sie diese Lösung verwenden, mit der Sie Folgendes tun können:

animals.sort {
  return comparisons(
    compare($0.family, $1.family, ascending: false),
    compare($0.name, $1.name))
}

Hier sind die Funktionen, mit denen Sie dies tun können:

func compare<C: Comparable>(_ value1Closure: @autoclosure @escaping () -> C, _ value2Closure: @autoclosure @escaping () -> C, ascending: Bool = true) -> () -> ComparisonResult {
  return {
    let value1 = value1Closure()
    let value2 = value2Closure()
    if value1 == value2 {
      return .orderedSame
    } else if ascending {
      return value1 < value2 ? .orderedAscending : .orderedDescending
    } else {
      return value1 > value2 ? .orderedAscending : .orderedDescending
    }
  }
}

func comparisons(_ comparisons: (() -> ComparisonResult)...) -> Bool {
  for comparison in comparisons {
    switch comparison() {
    case .orderedSame:
      continue // go on to the next property
    case .orderedAscending:
      return true
    case .orderedDescending:
      return false
    }
  }
  return false // all of them were equal
}

Wenn Sie es testen möchten, können Sie diesen zusätzlichen Code verwenden:

enum Family: Int, Comparable {
  case bird
  case cat
  case dog

  var short: String {
    switch self {
    case .bird: return "B"
    case .cat: return "C"
    case .dog: return "D"
    }
  }

  public static func <(lhs: Family, rhs: Family) -> Bool {
    return lhs.rawValue < rhs.rawValue
  }
}

struct Animal: CustomDebugStringConvertible {
  let name: String
  let family: Family

  public var debugDescription: String {
    return "\(name) (\(family.short))"
  }
}

let animals = [
  Animal(name: "Leopard", family: .cat),
  Animal(name: "Wolf", family: .dog),
  Animal(name: "Tiger", family: .cat),
  Animal(name: "Eagle", family: .bird),
  Animal(name: "Cheetah", family: .cat),
  Animal(name: "Hawk", family: .bird),
  Animal(name: "Puma", family: .cat),
  Animal(name: "Dalmatian", family: .dog),
  Animal(name: "Lion", family: .cat),
]

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.familystatt Animal.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 Methode sort/ sortedverwendet, jedoch zwei zusätzliche definiert werden muss: compareund comparisons.

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, es profileViewshandelt sich um einen teuren Netzwerkanruf, möchten wir möglicherweise einen Anruf vermeiden, es profileViewssei denn, dies ist unbedingt erforderlich. Meine Lösung wird es vermeiden, profileViewsbis $0.name == $1.nameund zu bewerten $0.address == $1.address. Wenn es jedoch ausgewertet wird, wird profileViewses wahrscheinlich viel öfter als einmal ausgewertet.

Sinnvoll
quelle
1

Wie wäre es mit:

contacts.sort() { [$0.last, $0.first].lexicographicalCompare([$1.last, $1.first]) }
Lou Zell
quelle
lexicographicallyPrecedeserfordert, 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].
Sinnvoll
-1

Das hat für mein Array [String] in Swift 3 funktioniert und es scheint in Swift 4 in Ordnung zu sein

array = array.sorted{$0.compare($1, options: .numeric) == .orderedAscending}
mamaz
quelle
Haben Sie die Frage vor der Beantwortung gelesen? Sortieren Sie nach mehreren Parametern, nicht nach einem, was Sie präsentieren.
Vive