Schnelles Sortieren von Arrays benutzerdefinierter Objekte nach Eigenschaftswerten

521

Nehmen wir an, wir haben eine benutzerdefinierte Klasse namens imageFile und diese Klasse enthält zwei Eigenschaften.

class imageFile  {
    var fileName = String()
    var fileID = Int()
}

viele von ihnen in Array gespeichert

var images : Array = []

var aImage = imageFile()
aImage.fileName = "image1.png"
aImage.fileID = 101
images.append(aImage)

aImage = imageFile()
aImage.fileName = "image1.png"
aImage.fileID = 202
images.append(aImage)

Die Frage ist: Wie kann ich das Bildarray nach 'fileID' ASC oder DESC sortieren?

Mohacs
quelle
sortieren nach KeyPath stackoverflow.com/a/46601105/2303865
Leo Dabus

Antworten:

941

Deklarieren Sie zunächst Ihr Array als typisiertes Array, damit Sie beim Iterieren Methoden aufrufen können:

var images : [imageFile] = []

Dann können Sie einfach tun:

Swift 2

images.sorted({ $0.fileID > $1.fileID })

Swift 3+

images.sorted(by: { $0.fileID > $1.fileID })

Das obige Beispiel gibt die Reihenfolge der Sortierung ab

Alex Wayne
quelle
1
Mir fehlte der Array-Deklarationsteil, er hat den Trick Array <imageFile> ausgeführt.
Mohacs
1
@AlexWayne Ich habe eine NSManagedObjectUnterklasse namens CheckInAndOut . In einer separaten Datei habe ich ein typisiertes Array für Objekte dieses Typs deklariert. Wenn ich versuche, es zu sortieren , wird der Fehler " Mitglied konnte nicht gefunden werden" angezeigt. Irgendeine Idee warum das so ist?
Isuru
3
Ich habe mein Problem gefunden. Anscheinend war das Array kein typisiertes Array. Jedenfalls habe ich ein neues Problem. Wie kann ich ein Array nach mehreren Eigenschaften sortieren? Angenommen, ich habe 2 Eigenschaften wie firstNameund lastNamein einem Array von PersonObjekten. Zuerst möchte ich es sortieren firstNameund dann lastName. Wie kann ich das machen?
Isuru
12
Müssen Sie jetzt tun images.sortInPlace({ $0.fileID > $1.fileID })?
Taylor M
13
Für den Fall, dass sich jemand das Gleiche fragt: Die Antwort gibt absteigende Reihenfolge
Danny Wang
223

[ Aktualisiert für Swift 3 mit sort (by :) ] Dies nutzt einen nachfolgenden Abschluss aus:

images.sorted { $0.fileID < $1.fileID }

wo Sie verwenden <oder >abhängig von ASC bzw. DESC. Wenn Sie das imagesArray ändern möchten , verwenden Sie Folgendes:

images.sort { $0.fileID < $1.fileID }

Wenn Sie dies wiederholt tun und lieber eine Funktion definieren möchten, haben Sie folgende Möglichkeiten:

func sorterForFileIDASC(this:imageFile, that:imageFile) -> Bool {
  return this.fileID > that.fileID
}

und dann verwenden als:

images.sort(by: sorterForFileIDASC)
GoZoner
quelle
Wie kann ich das mit String verklagen? Ich muss die Zeichenfolge nach ihrer Länge sortieren
Muneef M
@ MuneefM nur string1.length <string2.length zurückgeben
Surjeet Rajput
sortKompiliert nicht mehr mit dieser Syntax in Xcode 8. Xcode 8 sagt, dass $0.fileID < $1.fileIDein Bool nicht ComparisonResult erzeugt wird.
Crashalot
3
Der Code dieser Antwort funktioniert in Xcode8 einwandfrei. Wenn Sie einen Fehler haben, stellen Sie eine neue Frage.
GoZoner
Kann ich dies auch verwenden, um nach Vergleichen zu sortieren, z. B. indem das Array nach Wochentagen sortiert wird? Wenn das so ist, wie?
Kristofer
53

Fast jeder gibt an, wie direkt, lassen Sie mich die Entwicklung zeigen:

Sie können die Instanzmethoden von Array verwenden:

// general form of closure
images.sortInPlace({ (image1: imageFile, image2: imageFile) -> Bool in return image1.fileID > image2.fileID })

// types of closure's parameters and return value can be inferred by Swift, so they are omitted along with the return arrow (->)
images.sortInPlace({ image1, image2 in return image1.fileID > image2.fileID })

// Single-expression closures can implicitly return the result of their single expression by omitting the "return" keyword
images.sortInPlace({ image1, image2 in image1.fileID > image2.fileID })

// closure's argument list along with "in" keyword can be omitted, $0, $1, $2, and so on are used to refer the closure's first, second, third arguments and so on
images.sortInPlace({ $0.fileID > $1.fileID })

// the simplification of the closure is the same
images = images.sort({ (image1: imageFile, image2: imageFile) -> Bool in return image1.fileID > image2.fileID })
images = images.sort({ image1, image2 in return image1.fileID > image2.fileID })
images = images.sort({ image1, image2 in image1.fileID > image2.fileID })
images = images.sort({ $0.fileID > $1.fileID })

Ausführliche Erläuterungen zum Funktionsprinzip der Sortierung finden Sie unter Die sortierte Funktion .

fujianjin6471
quelle
Kann ich dies auch verwenden, um nach Vergleichen zu sortieren, z. B. indem das Array nach Wochentagen sortiert wird? Wenn das so ist, wie?
Kristofer
Vielen Dank, dass Sie eine Antwort veröffentlicht haben, die zeigt, wie ein Abschluss funktioniert, anstatt davon auszugehen, dass die Leser die kryptische Syntax des "vereinfachten" Abschlusses verstehen!
user1118321
50

Swift 3

people = people.sorted(by: { $0.email > $1.email })
sehr schön
quelle
Ich habe dies mit einem Datumsvergleich versucht, konnte es nicht zum Laufen bringen. Irgendeine Idee?
Ebru Güngör
Nicht NSDate oder String, aktuelles schnelles 3 Date-Objekt.
Ebru Güngör
Welche Eigenschaft von Date vergleichen Sie? Die Eigenschaft muss mit der verwendeten Funktion verglichen werden können (größer als in meinem Beispiel)
quemeful
9
Dies ist die einzig nützliche Antwort ab 2017.
Fattie
@ Fattie Was meinst du? Die richtige Syntax istpeople.sort { $0.email > $1.email }
Leo Dabus
43

Mit Swift 5 Arrayhat zwei Methoden aufgerufen sorted()und sorted(by:). Die erste Methode sorted()hat die folgende Deklaration:

Gibt die Elemente der Sammlung sortiert zurück.

func sorted() -> [Element]

Die zweite Methode sorted(by:)hat die folgende Deklaration:

Gibt die Elemente der Sammlung zurück, sortiert nach dem angegebenen Prädikat als Vergleich zwischen Elementen.

func sorted(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> [Element]

# 1. Sortieren Sie in aufsteigender Reihenfolge nach vergleichbaren Objekten

Wenn der Elementtyp in Ihrer Sammlung dem ComparableProtokoll entspricht , können Sie sorted()Ihre Elemente in aufsteigender Reihenfolge sortieren. Der folgende Spielplatzcode zeigt die Verwendung sorted():

class ImageFile: CustomStringConvertible, Comparable {

    let fileName: String
    let fileID: Int
    var description: String { return "ImageFile with ID: \(fileID)" }

    init(fileName: String, fileID: Int) {
        self.fileName = fileName
        self.fileID = fileID
    }

    static func ==(lhs: ImageFile, rhs: ImageFile) -> Bool {
        return lhs.fileID == rhs.fileID
    }

    static func <(lhs: ImageFile, rhs: ImageFile) -> Bool {
        return lhs.fileID < rhs.fileID
    }

}

let images = [
    ImageFile(fileName: "Car", fileID: 300),
    ImageFile(fileName: "Boat", fileID: 100),
    ImageFile(fileName: "Plane", fileID: 200)
]

let sortedImages = images.sorted()
print(sortedImages)

/*
 prints: [ImageFile with ID: 100, ImageFile with ID: 200, ImageFile with ID: 300]
 */

# 2. Sortieren Sie in absteigender Reihenfolge nach vergleichbaren Objekten

Wenn der Elementtyp in Ihrer Sammlung dem ComparableProtokoll entspricht , müssen Sie ihn verwenden sorted(by:), um Ihre Elemente in absteigender Reihenfolge zu sortieren.

class ImageFile: CustomStringConvertible, Comparable {

    let fileName: String
    let fileID: Int
    var description: String { return "ImageFile with ID: \(fileID)" }

    init(fileName: String, fileID: Int) {
        self.fileName = fileName
        self.fileID = fileID
    }

    static func ==(lhs: ImageFile, rhs: ImageFile) -> Bool {
        return lhs.fileID == rhs.fileID
    }

    static func <(lhs: ImageFile, rhs: ImageFile) -> Bool {
        return lhs.fileID < rhs.fileID
    }

}

let images = [
    ImageFile(fileName: "Car", fileID: 300),
    ImageFile(fileName: "Boat", fileID: 100),
    ImageFile(fileName: "Plane", fileID: 200)
]

let sortedImages = images.sorted(by: { (img0: ImageFile, img1: ImageFile) -> Bool in
    return img0 > img1
})
//let sortedImages = images.sorted(by: >) // also works
//let sortedImages = images.sorted { $0 > $1 } // also works
print(sortedImages)

/*
 prints: [ImageFile with ID: 300, ImageFile with ID: 200, ImageFile with ID: 100]
 */

#3. Für nicht vergleichbare Objekte in aufsteigender oder absteigender Reihenfolge sortieren

Wenn der Elementtyp in Ihrer Sammlung NICHT dem ComparableProtokoll entspricht, müssen Sie ihn verwenden sorted(by:), um Ihre Elemente in aufsteigender oder absteigender Reihenfolge zu sortieren.

class ImageFile: CustomStringConvertible {

    let fileName: String
    let fileID: Int
    var description: String { return "ImageFile with ID: \(fileID)" }

    init(fileName: String, fileID: Int) {
        self.fileName = fileName
        self.fileID = fileID
    }

}

let images = [
    ImageFile(fileName: "Car", fileID: 300),
    ImageFile(fileName: "Boat", fileID: 100),
    ImageFile(fileName: "Plane", fileID: 200)
]

let sortedImages = images.sorted(by: { (img0: ImageFile, img1: ImageFile) -> Bool in
    return img0.fileID < img1.fileID
})
//let sortedImages = images.sorted { $0.fileID < $1.fileID } // also works
print(sortedImages)

/*
 prints: [ImageFile with ID: 300, ImageFile with ID: 200, ImageFile with ID: 100]
 */

Beachten Sie, dass Swift auch zwei Methoden bereitstellt, die als sort()und sort(by:)als Gegenstücke zu sorted()und sorted(by:)wenn Sie Ihre Sammlung direkt sortieren müssen.

Imanou Petit
quelle
25

In Swift 3.0

images.sort(by: { (first: imageFile, second: imageFile) -> Bool in
    first. fileID < second. fileID
})
Jaiswal Rajan
quelle
20

Sie können auch so etwas tun

images = sorted(images) {$0.fileID > $1.fileID}

So wird Ihr Bildarray sortiert gespeichert

Nicolas Grenié
quelle
19

Swift 2 bis 4

In der ursprünglichen Antwort wurde versucht, ein Array von benutzerdefinierten Objekten mithilfe einer Eigenschaft zu sortieren. Im Folgenden werde ich Ihnen einige praktische Möglichkeiten zeigen, wie Sie dasselbe Verhalten mit schnellen Datenstrukturen ausführen können!

Kleinigkeiten aus dem Weg, ich habe ImageFile ganz leicht verändert. In diesem Sinne erstelle ich ein Array mit drei Bilddateien. Beachten Sie, dass Metadaten ein optionaler Wert sind und als Parameter nil übergeben wird.

 struct ImageFile {
      var name: String
      var metadata: String?
      var size: Int
    }

    var images: [ImageFile] = [ImageFile(name: "HelloWorld", metadata: nil, size: 256), ImageFile(name: "Traveling Salesmen", metadata: "uh this is huge", size: 1024), ImageFile(name: "Slack", metadata: "what's in this stuff?", size: 2048) ]

ImageFile hat eine Eigenschaft namens size. In den folgenden Beispielen werde ich Ihnen zeigen, wie Sie Sortieroperationen mit Eigenschaften wie Größe verwenden.

kleinste bis größte Größe (<)

    let sizeSmallestSorted = images.sorted { (initial, next) -> Bool in
      return initial.size < next.size
    }

größte bis kleinste (>)

    let sizeBiggestSorted = images.sorted { (initial, next) -> Bool in
      return initial.size > next.size
    }

Als nächstes sortieren wir nach dem Namen der String-Eigenschaft. Verwenden Sie auf die gleiche Weise sort, um Zeichenfolgen zu vergleichen. Beachten Sie jedoch, dass der innere Block ein Vergleichsergebnis zurückgibt. Dieses Ergebnis definiert die Sortierung.

AZ (.orderedAscending)

    let nameAscendingSorted = images.sorted { (initial, next) -> Bool in
      return initial.name.compare(next.name) == .orderedAscending
    }

ZA (.orderedDescending)

    let nameDescendingSorted = images.sorted { (initial, next) -> Bool in
      return initial.name.compare(next.name) == .orderedDescending
    }

Als nächstes sortiere ich am liebsten. In vielen Fällen verfügt man über optionale Eigenschaften. Jetzt mach dir keine Sorgen, wir werden auf die gleiche Weise wie oben sortieren, außer dass wir mit Null umgehen müssen! In Produktion;

Ich habe diesen Code verwendet, um zu erzwingen, dass alle Instanzen in meinem Array mit Null-Eigenschaftswerten zuletzt sind. Ordnen Sie dann Metadaten unter Verwendung der angenommenen nicht verpackten Werte.

    let metadataFirst = images.sorted { (initial, next) -> Bool in
      guard initial.metadata != nil else { return true }
      guard next.metadata != nil else { return true }
      return initial.metadata!.compare(next.metadata!) == .orderedAscending
    }

Es ist möglich, eine sekundäre Sortierung für Optionen zu haben. Zum Beispiel; man könnte Bilder mit Metadaten zeigen und nach Größe sortieren.

jnblanchard
quelle
1
Im Allgemeinen sind Antworten viel hilfreicher, wenn sie eine Erklärung enthalten, was der Code tun soll und warum dies das Problem löst, ohne andere einzuführen.
Tom Aranda
Viel viel besser.
Tom Aranda
18

Zwei Alternativen

1) Bestellen des ursprünglichen Arrays mit sortInPlace

self.assignments.sortInPlace({ $0.order < $1.order })
self.printAssignments(assignments)

2) Verwenden eines alternativen Arrays zum Speichern des geordneten Arrays

var assignmentsO = [Assignment] ()
assignmentsO = self.assignments.sort({ $0.order < $1.order })
self.printAssignments(assignmentsO)
Bernauer
quelle
3
Zu 2) Was bringt es, ein leeres Array zu erstellen und es in der nächsten Zeile zu verwerfen? Ich würde empfehlen var assignmentsO : [Assignment], es zu verwenden oder zu einer Zeile zu kombinieren, indem Sielet assignmentsO = self.assignments.sort({ $0.order < $1.order })
Hermann Klecker
2
Hallo Hermann! Es gibt eine sehr dünne Linie zwischen dem Schreiben von lesbarem und effizientem Code. In diesem Fall geht es nur darum, es für die Community lesbarer zu machen;) Viel Spaß!
Bernauer
18

Swift 4.0, 4.1 & 4.2 Zuerst habe ich ein veränderliches Array vom Typ imageFile () erstellt, wie unten gezeigt

var arr = [imageFile]()

Erstellen Sie ein veränderbares Objektbild vom Typ imageFile () und weisen Sie den Eigenschaften wie unten gezeigt einen Wert zu

   var image = imageFile()
   image.fileId = 14
   image.fileName = "A"

Hängen Sie dieses Objekt nun an das Array arr an

    arr.append(image)

Weisen Sie nun die verschiedenen Eigenschaften demselben veränderlichen Objekt zu, dh dem Bild

   image = imageFile()
   image.fileId = 13
   image.fileName = "B"

Fügen Sie nun erneut das Bildobjekt an das Array arr an

    arr.append(image)

Jetzt wenden wir aufsteigende Reihenfolge auf die fileId- Eigenschaft in Array-Arr-Objekten an. Verwenden Sie das Symbol < für aufsteigende Reihenfolge

 arr = arr.sorted(by: {$0.fileId < $1.fileId}) // arr has all objects in Ascending order
 print("sorted array is",arr[0].fileId)// sorted array is 13
 print("sorted array is",arr[1].fileId)//sorted array is 14

Jetzt wenden wir die absteigende Reihenfolge auf die Eigenschaft fileId in Array-Arr-Objekten an. Verwenden Sie das Symbol> für absteigende Reihenfolge

 arr = arr.sorted(by: {$0.fileId > $1.fileId}) // arr has all objects in Descending order
 print("Unsorted array is",arr[0].fileId)// Unsorted array is 14
 print("Unsorted array is",arr[1].fileId)// Unsorted array is 13

In Swift 4.1. & 4.2 Für sortierte Bestellungen verwenden

let sortedArr = arr.sorted { (id1, id2) -> Bool in
  return id1.fileId < id2.fileId // Use > for Descending order
}
Gurjinder Singh
quelle
8

Wenn Sie dieses Array an mehreren Stellen sortieren, ist es möglicherweise sinnvoll, Ihren Array-Typ vergleichbar zu machen.

class MyImageType: Comparable, Printable {
    var fileID: Int

    // For Printable
    var description: String {
        get {
            return "ID: \(fileID)"
        }
    }

    init(fileID: Int) {
        self.fileID = fileID
    }
}

// For Comparable
func <(left: MyImageType, right: MyImageType) -> Bool {
    return left.fileID < right.fileID
}

// For Comparable
func ==(left: MyImageType, right: MyImageType) -> Bool {
    return left.fileID == right.fileID
}

let one = MyImageType(fileID: 1)
let two = MyImageType(fileID: 2)
let twoA = MyImageType(fileID: 2)
let three = MyImageType(fileID: 3)

let a1 = [one, three, two]

// return a sorted array
println(sorted(a1)) // "[ID: 1, ID: 2, ID: 3]"

var a2 = [two, one, twoA, three]

// sort the array 'in place'
sort(&a2)
println(a2) // "[ID: 1, ID: 2, ID: 2, ID: 3]"
kwerle
quelle
6

Wenn Sie keine benutzerdefinierten Objekte verwenden, sondern stattdessen Werttypen, die das vergleichbare Protokoll (Int, String usw.) implementieren, können Sie dies einfach tun:

myArray.sort(>) //sort descending order

Ein Beispiel:

struct MyStruct: Comparable {
    var name = "Untitled"
}

func <(lhs: MyStruct, rhs: MyStruct) -> Bool {
    return lhs.name < rhs.name
}
// Implementation of == required by Equatable
func ==(lhs: MyStruct, rhs: MyStruct) -> Bool {
    return lhs.name == rhs.name
}

let value1 = MyStruct()
var value2 = MyStruct()

value2.name = "A New Name"

var anArray:[MyStruct] = []
anArray.append(value1)
anArray.append(value2)

anArray.sort(>) // This will sort the array in descending order
dorian
quelle
in Swift 3 ist esmyArray.sorted(by: >)
Beryllium
6

Sie geben ein sortiertes Array aus der fileID-Eigenschaft folgendermaßen zurück:

Swift 2

let sortedArray = images.sorted({ $0.fileID > $1.fileID })

Swift 3 ODER 4

let sortedArray = images.sorted(by: { $0.fileID > $1.fileID })

Swift 5.0

let sortedArray = images.sorted {
    $0.fileID < $1.fileID
}
Vicky Prajapati
quelle
Funktioniert wie ein Zauber .. upvoted! (Pratik Prajapati, Ahmedabad)
NSPratik
4

Ich mache es so und es funktioniert:

var images = [imageFile]() images.sorted(by: {$0.fileID.compare($1.fileID) == .orderedAscending })

Illya Krit
quelle
2

Wenn Sie das ursprüngliche Array von benutzerdefinierten Objekten sortieren möchten. Hier ist eine andere Möglichkeit, dies in Swift 2.1 zu tun

var myCustomerArray = [Customer]()
myCustomerArray.sortInPlace {(customer1:Customer, customer2:Customer) -> Bool in
    customer1.id < customer2.id
}

Wo idist eine ganze Zahl? Sie können denselben <Operator auch für StringEigenschaften verwenden.

Weitere Informationen zur Verwendung finden Sie in einem Beispiel hier: Swift2: Kunden in der Nähe

Hanny
quelle
2
var students = ["Kofi", "Abena", "Peter", "Kweku", "Akosua"]

students.sort(by: >)

print(students)

Drucke: "["Peter", "Kweku", "Kofi", "Akosua", "Abena"]"

Siddharth Chauhan
quelle
1

Schnelle 3 & 4 & 5

Ich hatte ein Problem mit Klein- und Großbuchstaben

Also habe ich diesen Code gemacht

let sortedImages = images.sorted(by: { $0.fileID.lowercased() < $1.fileID.lowercased() })

und verwenden Sie danach sortierte Bilder

Abdelrahman Mohamed
quelle
0

Sortieren mit KeyPath

Sie können folgendermaßen sortieren KeyPath:

myArray.sorted(by: \.fileName, <) /* using `<` for ascending sorting */

Durch die Implementierung dieser kleinen hilfreichen Erweiterung.

extension Collection{
    func sorted<Value: Comparable>(
        by keyPath: KeyPath<Element, Value>,
        _ comparator: (_ lhs: Value, _ rhs: Value) -> Bool) -> [Element] {
        sorted { comparator($0[keyPath: keyPath], $1[keyPath: keyPath]) }
    }
}

Hoffe, Swift fügt dies in naher Zukunft in den Kern der Sprache ein.

Mojtaba Hosseini
quelle
Dies wurde hier bereits gestapelt , stackoverflow.com/a/46601105/2303865 zusammen mit der Mutationsmethode.
Leo Dabus
die mutierende Versionpublic extension MutableCollection where Self: RandomAccessCollection { mutating func sort<T>(_ keyPath: KeyPath<Element, T>, by areInIncreasingOrder: (T, T) throws -> Bool) rethrows where T: Comparable { try sort { try areInIncreasingOrder($0[keyPath: keyPath], $1[keyPath: keyPath]) } }}
Leo Dabus