Compare Day/Month in Swift Struct - swift

I would like sort an array of SpecialDay instances: [struct1, struct2, struct3, ...] using the code below:
struct SpecialDay: Hashable, Comparable {
var day: Int
var month: Int
var hashValue: Int {
return (31 &* day.hashValue &+ month.hashValue)
}
}
func ==(lhs: SpecialDay, rhs: SpecialDay) -> Bool {
return lhs.day == rhs.day && lhs.month == rhs.month
}
func <(lhs: SpecialDay, rhs: SpecialDay) -> Bool {
return lhs.day < rhs.day && lhs.month <= rhs.month
}
Sorting would be done like this:
let newArray = currentArray.sorted({ $0 < $1 })
I think I only need to find the right logic behind the comparable method:
func <(lhs: SpecialDay, rhs: SpecialDay) -> Bool
... but I am pulling my teeth on this one. My currently implemented logic is obviously not sorting correctly.
Any hints would be greatly appreciated.

The problem is the and in this line:
return lhs.day < rhs.day && lhs.month <= rhs.month
If the lhs month is less than the rhs it should always return true not taking into account the day. However, when you and with the comparison to days, you can get a false for the days not being less and a true for the months, which results in a false. You need something just a little more complicated:
func <(lhs: SpecialDay, rhs: SpecialDay) -> Bool {
if lhs.month < rhs.month { return true }
else if lhs.month > rhs.month { return false }
else {
return lhs.day < rhs.day
}
}

Related

Sort array in swiftui by multiple conditions

how it is possible to sort an array by multiple conditions.
struct UserInformationModel: Identifiable, Hashable {
let id = UUID()
var isVip: Bool
let userIsMale: Bool
let userName: String
let age: Int
let userCountry: String
let countryIsoCode: String
let uid: String
}
And the view model contain the code:
class GetUserInformationViewModel: ObservableObject {
#Published var allUsers = [UserInformationModel]()
fun sortmyarray(){
self.allUsers = self.allUsers.sorted(by: {$0.isVip && !$1.isVip})
}
how its possible to sort first the vip users, and then sort by age and then country?
Here is a simple way to sort on multiple properties (I have assumed a sort order here for each property since it isn't mentioned in the question)
let sorted = users.sorted {
if $0.isVip == $1.isVip {
if $0.age == $1.age {
return $0.userCountry < $1.userCountry
} else {
return $0.age < $1.age
}
}
return $0.isVip && !$1.isVip
}
If the above is the natural sort order for the type then you could let the type implement Comparable and implement the < func
struct UserInformationModel: Identifiable, Hashable, Comparable {
//properties removed for brevity
static func < (lhs: UserInformationModel, rhs: UserInformationModel) -> Bool {
if lhs.isVip == rhs.isVip {
if lhs.age == rhs.age {
return lhs.userCountry < rhs.userCountry
} else {
return lhs.age < rhs.age
}
}
return lhs.isVip && !rhs.isVip
}
}
and then the sorting would be
let sorted = users.sorted()
Use tuples.
allUsers.sorted {
($1.isVip.comparable, $0.age, $0.userCountry)
<
($0.isVip.comparable, $1.age, $1.userCountry)
}
public extension Bool {
/// A way to compare `Bool`s.
///
/// Note: `false` is "less than" `true`.
enum Comparable: CaseIterable, Swift.Comparable {
case `false`, `true`
}
/// Make a `Bool` `Comparable`, with `false` being "less than" `true`.
var comparable: Comparable { .init(booleanLiteral: self) }
}
extension Bool.Comparable: ExpressibleByBooleanLiteral {
public init(booleanLiteral value: Bool) {
self = value ? .true : .false
}
}

How to sort timestamps in the dictionary object

I am trying to write a LRU cache and creates an object and check which item is added first by looking at the timestamps as follows.
import Foundation
class Item : Comparable
{
var timeStamp : Date
var value : Int
init(_ value: Int)
{
self.value = value
self.timeStamp = Date()
}
}
func < (lhs: Item, rhs: Item) -> Bool {
return lhs.timeStamp < rhs.timeStamp
}
func == (lhs: Item, rhs: Item) -> Bool {
return lhs.timeStamp == rhs.timeStamp
}
var item1 = Item (1)
var item2 = Item (2)
var item3 = Item (3)
var items = [1: item1 , 2 : item2, 3: item3]
items = items.sorted(by: {$0.date.compare($1.date) == .orderedDescending})
print(items)
I am getting the following issue:
error: value of tuple type '(key: Int, value: Item)' has no member
'date' items = items.sorted(by: {$0.date.compare($1.date) ==
.orderedDescending})
Then I need to sort and find the earliest timestamps in the dictionary.
You can simply use the < operator to compare Dates.
let sortedItems = items.sorted(by: {$0.value.timeStamp < $1.value.timeStamp})
I wouldn't recommend implementing Comparable, since even though my might want to sort your items based on simply their timestamps in some scenarios, it seems like you should take into consideration the value as well, especially for equality checking in other scenarios.
If you really only need to check timeStamp for comparison, you can make Item conform to Comparable and shorten your closure for sorted.
extension Item: Comparable {
static func == (lhs: Item, rhs: Item) -> Bool {
return lhs.timeStamp == rhs.timeStamp
}
static func < (lhs: Item, rhs: Item) -> Bool {
return lhs.timeStamp < rhs.timeStamp
}
}
let sortedItems = items.sorted(by: {$0.value < $1.value})
Just implement Comparable protocol for Item, or, maybe more useful
[1: Item(1),2: Item(2),2: Item(3),].sorted { $0.value.timeStamp < $1.value.timeStamp }

Is there a stable sort that works with partial orders in the standard library? [duplicate]

I've been using the sort() function but it mixes up the relative order.
This is how my code looks.
recipes.sort { $0.skill.value <= $1.skill.value }
Swift API says that:
The sorting algorithm is not stable. A nonstable sort may change the
relative order of elements that compare equal.
How can I change this so that the relative order stays the same as before?
The implementation below just work like the sorted method in the standard library, without additional limit.
extension RandomAccessCollection {
/// return a sorted collection
/// this use a stable sort algorithm
///
/// - Parameter areInIncreasingOrder: return nil when two element are equal
/// - Returns: the sorted collection
public func stableSorted(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> [Element] {
let sorted = try enumerated().sorted { (one, another) -> Bool in
if try areInIncreasingOrder(one.element, another.element) {
return true
} else {
return one.offset < another.offset
}
}
return sorted.map { $0.element }
}
}
A stable sort needs to preserve the original order. So we give every element a weight of order besides its value, the index, then the original sort method will just work, as there will never be 2 equal elements.
I appreciate the elegance of leavez's answer. I adapted it to have the same signature as Sequence.sorted(by:):
extension Sequence {
func stableSorted(
by areInIncreasingOrder: (Element, Element) throws -> Bool)
rethrows -> [Element]
{
return try enumerated()
.sorted { a, b -> Bool in
try areInIncreasingOrder(a.element, b.element) ||
(a.offset < b.offset && !areInIncreasingOrder(b.element, a.element))
}
.map { $0.element }
}
}
let sortedArray = (recipes as NSArray).sortedArray(options: .stable, usingComparator: { (lhs, rhs) -> ComparisonResult in
let lhs = (lhs as! Recipe)
let rhs = (rhs as! Recipe)
if lhs.skill.value == rhs.skill.value {
return ComparisonResult.orderedSame
} else if lhs.skill.value < rhs.skill.value {
return ComparisonResult.orderedAscending
} else {
return ComparisonResult.orderedDescending
}
})
Took from here: https://medium.com/#cocotutch/a-swift-sorting-problem-e0ebfc4e46d4
In Swift 5 sort() uses stable implementation and soon it will become officially guaranted to be stable.
From Swift forums:
...
On the other hand, the actual implementation calls
/// Sorts the elements of this buffer according to `areInIncreasingOrder`,
/// using a stable, adaptive merge sort.
///
/// The adaptive algorithm used is Timsort, modified to perform a straight
/// merge of the elements using a temporary buffer.
#inlinable
public mutating func _stableSortImpl(
by areInIncreasingOrder: (Element, Element) throws -> Bool
) rethrows { ... }
And
If I recall, sort() is currently stable, but it is not yet guaranteed
to be stable (meaning, the fact that it is stable is currently an
implementation detail, and a future version of Swift could ship an
unstable algorithm instead).
I use this wrapper
extension Array where Element: Comparable, Element: AnyObject {
public func stableSorted() -> [Element] {
let array = self as NSArray
let result = array.sortedArray(options: .stable) { (left, right) -> ComparisonResult in
let left = left as! Element
let right = right as! Element
if left < right {
return ComparisonResult.orderedAscending
}
if left > right {
return ComparisonResult.orderedDescending
}
return ComparisonResult.orderedSame
}
return result as! [Element]
}
public func stableReversed() -> [Element] {
let array = self as NSArray
let result = array.sortedArray(options: .stable) { (left, right) -> ComparisonResult in
let left = left as! Element
let right = right as! Element
if left > right {
return ComparisonResult.orderedAscending
}
if left < right {
return ComparisonResult.orderedDescending
}
return ComparisonResult.orderedSame
}
return result as! [Element]
}
}
I appreciate the elegance of Tom's answer. Harking back to my Perl days I've reworked it to use ComparisonResult and the spaceship (<=>) operator:
extension Sequence {
func sorted(with comparator: (Element, Element) throws -> ComparisonResult) rethrows -> [Element]
{
return try enumerated()
.sorted { (try comparator($0.element, $1.element) || $0.offset <=> $1.offset) == .orderedAscending }
.map { $0.element }
}
}
extension Comparable {
static func <=> (lhs: Self, rhs: Self) -> ComparisonResult {
if lhs < rhs { return .orderedAscending }
if lhs > rhs { return .orderedDescending }
return .orderedSame
}
}
extension ComparisonResult {
static func || (lhs: Self, rhs: #autoclosure () -> Self) -> ComparisonResult {
if lhs == .orderedSame {
return rhs()
}
return lhs
}
}

Ternary operator in Swift Error

Why does this code gives error(Swift 2.2) :
Expected Expresion
at return line
func == (lhs: Employee, rhs: Employee) -> Int {
return (lhs.empName == rhs.empName && lhs.empCode == rhs.empCode)?1:0
}
There's no need to use the ternary operator here — x ? true : false is exactly the same as x. I'd write:
func ==(lhs: Employee, rhs: Employee) -> Bool {
return lhs.empName == rhs.empName && lhs.empCode == rhs.empCode
}
Silly. There has to be a space between the BOOL being checked upon and the ?
So flag?expressionA:expressionB won't work.
Instead flag ?expressionA:expressionB will work.
Maybe compiler assumes flag? as optional chaining.
This works
func == (lhs: Employee, rhs: Employee) -> Int {
return (lhs.empName == rhs.empName && lhs.empCode == rhs.empCode) ?1:0
}

Defining `Comparable` for optional types in Swift 1.2

import SwiftyJSON
public typealias FeedItem = JSON
extension FeedItem {
var id: Int { get { return self["id"].intValue } }
}
public func <(lhs: FeedItem, rhs: FeedItem) -> Bool {
return lhs.id < rhs.id
}
public func ==(lhs: FeedItem, rhs: FeedItem) -> Bool {
return lhs.id == rhs.id
}
extension FeedItem:Comparable {}
extension Optional : Comparable {}
public func < <T>(l:T?, r:T?) -> Bool {
if let a=l,b=r {
return a < b
} else if (l==nil) && (r != nil) { return true }
else { return false }
}
public func == <T>(l:T?, r:T?) -> Bool {
if let a=l, b=r {
return a==b
} else {
return false
}
}
First, it should read extension Optional<T where T:Comparable>: Comparable but swift 1.2 does not allow that. Anyway I can express the constraint more explicitly rather than expecting the reader to realize the fact by noticing return a < b and return a==b ?
Second (and apparently more important): the code above works when I use < and > but minElement and maxElement both return nil when a nil is present in their input no matter what and min and max fall into infinite recursion:
let items: [FeedItem?] = [ nil, .Some(JSON(["id":2])), .Some(JSON(["id":1])) ]
println(items[0]?.id) // nil
println(items[1]?.id) // Optional(2)
println(items[2]?.id) // Optional(1)
println(items[0] < items[1]) // true
println(items[1] < items[2]) // false
println(items[2] < items[1]) // true
println(minElement(items)) // nil
println(maxElement(items)) // nil
println( min(items[0],items[2]) ) // nil
println( min(items[2],items[1]) ) // crashes due to infinite recursion
I'm no debug (or swift) expert but from what I can gather in XCode I believe the
if let a=l,b=r {
return a < b
}
part somehow misses the point that a and b are not Optionals but FeedItems. I expect the a < b should call the < operator on FeedItem and not the one on Optional but apparently this is exactly what happens; i.e. a < b resolves to the same function it is called from (that is, the < for Optionals) and thus a recursion happens. I might be wrong though.
Insights ?