2014-11-03 10 views
5

Ich habe eine Liste von Swift-Objekten, die ich gerne nach mehreren Kriterien sortiert bekommen würde. Die Objekte in der Liste sind vom Typ DateRange:Wie sortiere ich Swift-Objekte nach mehreren Kriterien

class DateRange { 
    var from: NSDate? 
    var to: NSDate? 
} 

Die Liste viele dieser Objekte enthält, wo einige from oder to Felder Null sind. Ich möchte diese Liste sortiert haben von:

  1. zuerst alle Objekte, die Daten
  2. dann Objekte, die mindestens ein Datum haben (entweder from oder to)
  3. Und ganz am Ende Objekte ohne

Die Daten selbst sind nicht wichtig, nur ihre Existenz. In Ruby könnte ich dies tun (wenn das Datum nil ist Ich stelle es auf ein sehr niedriges Datum):

date_ranges.sort { |a, b| 
    [fix_nil(a.from), fix_nil(a.to)] <=> [fix_nil(b.from), fix_nil(b.to)] 
}.reverse 

def fix_nil(val) 
    val.nil? ? Date.new(0) : val 
end 

Was ist der beste Weg, dies mit Swift zu tun? Danke im Voraus.

Antwort

2

Scheint, wie es könnte eine gute Idee sein, eine dateCount berechnete Eigenschaft zu Ihrem DateRange Typ hinzuzufügen. Dies wäre eine gute Zeit für den Mustervergleich sein:

extension DateRange { 
    // returns the number of non-nil NSDate members in 'from' and 'to' 
    var dateCount: Int { 
     switch (from, to) { 
     case (nil, nil): return 0 
     case (nil, _): return 1 
     case (_, nil): return 1 
     default: return 2 
     } 
    } 
} 

Dann können Sie Ihre Liste mit einem einfachen Verschluss sortieren:

var ranges = [DateRange(nil, nil), DateRange(NSDate(), nil), DateRange(nil, NSDate()), DateRange(nil, nil), DateRange(NSDate(), NSDate())] 
ranges.sort { $0.dateCount > $1.dateCount } 

Wenn Sie möchten, können Sie es sogar machen Comparable mit ein paar mehr Zeilen :

extension DateRange : Comparable { } 
func ==(lhs: DateRange, rhs: DateRange) -> Bool { 
    return lhs.dateCount == rhs.dateCount 
} 
func <(lhs: DateRange, rhs: DateRange) -> Bool { 
    return lhs.dateCount > rhs.dateCount 
} 

Auf diese Weise können Sie sortieren Sie Ihre Liste ordnungsgemäß mit einem Operator Argument:

ranges.sort(<) 
+0

Großer Anwendungsfall für Mustervergleich, danke für Ihre Hilfe! –

1

Ich nehme an, dass durch Liste meinen Sie Array, so dass ich auf dieser Annahme meine Antwort zu stützen.

Sie können die sort Methode des Arrays struct verwenden, die einen Verschluss aufweist, nimmt diese Signatur:

(lhs: T, rhs: T) -> Bool 

Rückkehr true wenn lhs kleiner als rhs, ansonsten false.

kam ich mit dieser Implementierung bis:

var x: [DateRange] 
// ... initialize the array 

x.sort { (lhs: DateRange, rhs: DateRange) -> Bool in 
    if lhs.from != nil && lhs.to != nil { 
     return true 
    } 

    if lhs.from == nil && lhs.to == nil { 
     return false 
    } 

    return rhs.from == nil && rhs.to == nil 
} 
  • wenn lhs beide Eigenschaften nicht null hat, dann kommt es zuerst, und zwar unabhängig von rhs
  • wenn lhs hat beide Eigenschaften nil, dann, wenn nach kommt unabhängig von rhs
  • sonst lhs hat eine Null, die andere nicht Null, und in diesem Fall kommt es zuerst nur, wenn rhs beide Eigenschaften Null hat

Wenn Sie die sort an mehreren Stellen wiederverwenden möchten, ist es besser, den Code aus dem sort Methode zu bewegen - der beste Ort ist wahrscheinlich eine Überlastung des < Betreiber:

func < (lhs: DateRange, rhs: DateRange) -> Bool { 
    if lhs.from != nil && lhs.to != nil { 
     return true 
    } 

    if lhs.from == nil && lhs.to == nil { 
     return false 
    } 

    return rhs.from == nil && rhs.to == nil 
} 

und in Dabei kann es wie folgt verwendet werden:

x.sort(<) 

Wenn Sie den Operator Überlastung nicht gefällt, können Sie uns selbstverständlich, dass die Funktion einen anderen Namen geben.

Beachten Sie, dass die Sortierung statt erfolgt.

+0

Ich denke, dass Ihr Vergleich einen Fehler hat - das wird wahr, wenn zwei gleiche 'DateRange'-Instanzen verglichen werden, wenn beide" (nil, nil) "sind. –

+0

Das sollte von der 2nd abgedeckt werden, wenn: Wenn lhs beide Null hat, gibt es false – Antonio

+0

Sorry - ich habe das rückwärts. Wenn beide Bereiche beide Daten haben, wird dies als wahr zurückgegeben, da diese erste Bedingung nur "lhs" betrachtet. –

1

Hier ist, wie ich das angehen würde. Um die Dinge einfach zu halten, fügen Sie eine Bewertungsfunktion für den Datumsbereich hinzu. In Ihrem Szenario haben Sie 3 Möglichkeiten:

nil & nil: 0 Punkte

nil & Datum: 1 Punkt

Datum & Datum: 2 Punkte

import Foundation 

class DateRange { 
    var from: NSDate? 
    var to: NSDate? 

    init(from: NSDate?, to: NSDate?) 
    { 
     self.from = from 
     self.to = to 
    } 

    func scoreDateRange() -> Int 
    { 
     var score = 0 
     if from != nil 
     { 
      score++ 
     } 
     if to != nil 
     { 
      score++ 
     } 
     return score 
    } 
} 

func sortDateRange(d1 : DateRange, d2 : DateRange)-> Bool 
{ 

    return d1.scoreDateRange() > d2.scoreDateRange() 
} 

var date_ranges = [DateRange]() 
date_ranges.append(DateRange(from:nil, to:nil)) 
date_ranges.append(DateRange(from:nil, to:nil)) 
date_ranges.append(DateRange(from:NSDate(), to:NSDate())) 
date_ranges.append(DateRange(from:nil, to:NSDate())) 
date_ranges.append(DateRange(from:NSDate(), to:nil)) 
date_ranges.append(DateRange(from:NSDate(), to:NSDate())) 

date_ranges.sort(sortDateRange) 
+0

Schöne Idee mit der Scoring-Funktion, danke für Ihre schnelle Antwort! –

Verwandte Themen