Defining `Comparable` for optional types in Swift 1.2 - swift

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 ?

Related

class AnyComparable inherits from class AnyEquatable

public class AnyEquatable: Equatable {
public let value: Any
private let equals: (Any) -> Bool
public init<E: Equatable>(_ value: E) {
self.value = value
self.equals = { $0 as? E == value }
}
public static func == (lhs: AnyEquatable, rhs: AnyEquatable) -> Bool {
lhs.equals(rhs.value) || rhs.equals(lhs.value)
}
}
public class AnyComparable: AnyEquatable, Comparable {
private let compares: (Any) -> Bool
public init<C: Comparable>(_ value: C) {
super.init(value)
self.compares = { ($0 as? C).map { $0 < value } ?? false }
}
public static func < (lhs: AnyComparable, rhs: AnyComparable) -> Bool {
lhs.compares(rhs.value) || rhs.compares(lhs.value)
}
}
I've got an error:
Overridden method 'init' has generic signature C where C:
Comparable which is incompatible with base method's generic signature
E where E: Equatable; expected generic signature to be C where C:
Equatable
Why does the compiler think that I'm overriding initializer? I didn't use the word "override". I can't override here because the method 'init' has different signature (which the compiler tells itself). It's not an overriding. I want the subclass to use its own initilizer, and I (obviously) don't want inherit superclass initializer.
So, how can I achieve that? What can be more natural than something comparable inherits from something equatable! Why can't I do that?
Note that your initialiser in the subclass does override the initialiser in the superclass. If you fix the constraint on C as the compiler asks you to, the next thing the compiler will tell you, is that you are missing an override keyword.
This behaviour is quite consistent, so it seems like it is deliberately designed that the generic constraints are not taken into account when determining whether one method overrides another. Here's another example of this:
class A {
func f<T>(t: T) {}
}
class B: A {
func f<C: Equatable>(t: C) {} // error
}
Anyway, one way you can work around this is to just change the argument label:
public init<C: Comparable>(comparable value: C) {
self.compares = { ($0 as? C).map { $0 < value } ?? false }
super.init(value)
}
The downside of this is that the client code gets a bit less concise. You have to do AnyComparable(comparable: something) every time, which is quite long. One solution is to put both AnyEquatable and AnyComparable in the same file, make both initialisers fileprivate. You can then provide factory functions (in the same file) that create these classes. For example:
public extension Equatable {
func toAnyEquatable() -> AnyEquatable {
.init(equatable: self)
}
}
public extension Comparable {
func toAnyComparable() -> AnyComparable {
.init(comparable: self)
}
}
Also note that your implementation of < doesn't make much sense. It should be:
public static func < (lhs: AnyComparable, rhs: AnyComparable) -> Bool {
lhs.compares(rhs.value) || (rhs != lhs && !rhs.compares(lhs.value))
}
and compares should mean "less than",
self.compares = { ($0 as? C).map { value < $0 } ?? false }
to say "lhs < rhs or rhs > lhs".
But even still, there are cases where using this class with completely unrelated types still doesn't make much sense, just FYI:
let a = AnyComparable(comparable: "foo")
let b = AnyComparable(comparable: 1)
print(a < b) // true
print(a > b) // true
print(a <= b) // false
print(a >= b) // false
print(a == b) // false
So please do be careful!

Enum pattern matching as a parameter to a function call

I've setup a playground with an example:
enum CarType : Equatable {
case wheeled(wheels: Int)
case flying
public static func ==(lhs: CarType, rhs: CarType) -> Bool {
return lhs.enumName == rhs.enumName
}
var enumName: String {
let stuff = "\(self)".split(separator: "(").first!
return String(describing: stuff)
}
}
var typesPresentAtMyParty = [CarType.wheeled(wheels:4), .wheeled(wheels:4), .flying]
let aKnownType = CarType.flying
if case aKnownType = typesPresentAtMyParty[2] {
print("Was the type")
}
func isPresent(type: CarType, inArray: [CarType]) -> Bool {
return inArray.filter {
if case type = $0 {
return true
}
return false
}.first != nil
}
func isWheeled(inArray: [CarType]) -> Bool {
return inArray.filter {
if case .wheeled = $0 {
return true
}
return false
}.first != nil
}
isPresent(type: .flying, inArray: typesPresentAtMyParty)
isPresent(type: .wheeled, inArray: typesPresentAtMyParty)
The last line here does not compile. While i can do if case .wheeled = $0 ignoring associated type as a check, i cannot find a way of doing the same in a function call isPresent(type: CarType, inArray: [CarType]), when sending isPresent(type: .wheeled, inArray: typesPresentAtMyParty)
Is there a way of writing a function that takes only the valid pattern matching part of the enum as a parameter?
It is not possible to pass partially constructed enums to a function. Partially constructed enums are not valid values, and they only work in pattern matching because the compiler has a concrete value to work with - the one from the right side of the pattern.
These being said, you could easily rewrite your functions to better, more swiftier versions.
Firstly, you don't need isPresent, you can simply use contains:
typesPresentAtMyParty.contains { $0 == .flying }
typesPresentAtMyParty.contains { if case . wheeled = $0 { return true } else { return false } }
Similarly, isWheeled can be shortened (and renamed, for better semantics):
func isWheeled(_ carType: CarType) -> Bool {
if case . wheeled = carType { return true } else { return false }
}
which can pe passed to contains:
let hasWeeled = typesPresentAtMyParty.contains(where: isWheeled)

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

Compare Day/Month in Swift Struct

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

Comparing objects in an Array extension causing error in Swift

I'm trying to build an extension that adds some of the convenience functionality of NSArray/NSMutableArray to the Swift Array class, and I'm trying to add this function:
func indexOfObject(object:AnyObject) -> Int? {
if self.count > 0 {
for (idx, objectToCompare) in enumerate(self) {
if object == objectToCompare {
return idx
}
}
}
return nil
}
But unfortunately, this line:
if object == objectToCompare {
Is giving the error:
could not find an overload for '==' that accepts the supplied arguments
Question
What am I doing wrong to cause this error?
Example
extension Array {
func indexOfObject(object:AnyObject) -> Int? {
if self.count > 0 {
for (idx, objectToCompare) in enumerate(self) {
if object == objectToCompare {
return idx
}
}
}
return nil
}
}
Actually there is no need to implement indexOfObject:; there is a global function find(array, element) already.
You can always create an extension that uses NSArray's indexOfObject, e.g:
extension Array {
func indexOfObject(object:AnyObject) -> Int? {
return (self as NSArray).indexOfObject(object)
}
}
You can specify that your array items can be compared with the <T : Equatable> constraint, then you can cast your object into T and compare them, e.g:
extension Array {
func indexOfObject<T : Equatable>(o:T) -> Int? {
if self.count > 0 {
for (idx, objectToCompare) in enumerate(self) {
let to = objectToCompare as T
if o == to {
return idx
}
}
}
return nil
}
}
My guess is that you have to do something like this:
func indexOfObject<T: Equatable>(object: T) -> Int? {
and so on.
Here's a relevant example from Apple's "The Swift Programming Language" in the "Generics" section:
func findIndex<T: Equatable>(array: T[], valueToFind: T) -> Int? {
for (index, value) in enumerate(array) {
if value == valueToFind {
return index
}
}
return nil
}
The key idea here is that both value and valueToFind must of a type that is guaranteed to have the == operator implemented/overloaded. The <T: Equatable> is a generic that allows only objects of a type that are, well, equatable.
In your case, we would need to ensure that the array itself is composed only of objects that are equatable. The Array is declared as a struct with a generic <T> that does not require it to be equatable, however. I don't know whether it is possible to use extensions to change what kind of types an array can be composed of. I've tried some variations on the syntax and haven't found a way.
You can extract the compare part to another helper function, for example
extension Array {
func indexOfObject(object: T, equal: (T, T) -> Bool) -> Int? {
if self.count > 0 {
for (idx, objectToCompare) in enumerate(self) {
if equal(object, objectToCompare) {
return idx
}
}
}
return nil
}
}
let arr = [1, 2, 3]
arr.indexOfObject(3, ==) // which returns {Some 2}
You were close. Here's a working extension:
extension Array {
func indexOfObject<T: Equatable>(object:T) -> Int? {
if self.count > 0 {
for (idx, objectToCompare) in enumerate(self) {
if object == objectToCompare as T {
return idx
}
}
}
return nil
}
}
Swift had no way of knowing if object or objectToCompare were equatable. By adding generic information to the method, we're then in business.