How to sort Swift objects by several criteria

I have a list of Swift objects that I would like to sort by several criteria. The objects in the list are of type DateRange :

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

The list contains many of these objects, where some from or to fields are zero. I want this list sorted by:

  • First all objects having dates
  • Then objects having at least one date (either from , or to )
  • And at the very end of the objects without any

Dates themselves do not matter, just their existence. In Ruby, I could do this (if the date is nil , I set it to a very low date):

 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 

What is the best way to do this with Swift? Thanks in advance.

+5
source share
3 answers

It seems like it would be nice to add the computed dateCount property to your DateRange type. This would be a good time to match patterns:

 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 } } } 

Then you can sort the list with a simple close:

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

If you want, you can even make it Comparable with a few more lines:

 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 } 

This allows you to correctly sort the list with the operator argument:

 ranges.sort(<) 
+2
source

I assume that by list you mean an array, so I base my answer on this assumption.

You can use the sort method for a struct array that accepts a closure that has this signature:

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

return true if lhs less than rhs , otherwise false.

I came up with this implementation:

 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 } 
  • if lhs has both non-zero properties, then it lhs first, regardless of rhs
  • if lhs has both nil properties, then if after, regardless of rhs
  • else lhs has one nil value, the other is not nil, and in this case it comes first only if rhs has both nil properties

If you plan to reuse sort in several places, it is better to move the code from the sort method - the best place is probably overloading the < operator:

 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 } 

and in this case it can be used as follows:

 x.sort(<) 

If you don't like operator overloading, you can, of course, give this function any other name.

Please note that sorting is done locally.

+1
source

This is how I approach this. To keep things simple, add a count function for the date range. In your scenario, you have 3 possibilities:

nil and nil: 0 points

nil and date: 1 point

date and date: 2 points

 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) 
+1
source

Source: https://habr.com/ru/post/1206132/


All Articles