I am experiencing with protocols and challenged myself to write a code snippet that overloads the == operator so that it returns true when I compare a random String with value "42" with a Int of value 42. Please don't question the usefulness by simply returning 42 on a String, the main point is getting the Equality Operator to run on the two different types.
Here is what I tried:
Version 1
import Foundation
protocol IntTransformable: Equatable {
func toInt() -> Int
}
extension String: IntTransformable {
func toInt() -> Int {
return 42
}
}
extension Int: IntTransformable {
func toInt() -> Int {
return self
}
}
extension IntTransformable {
static func == (lhs: Self, rhs: Self) -> Bool {
return lhs.toInt() == rhs.toInt()
}
}
// throws: Ambiguous reference to operator function '=='
if "42" == 42 {
print("equal")
} else {
print("unequal")
}
Version 2
import Foundation
protocol IntTransformable: Equatable {
func toInt() -> Int
}
extension String: IntTransformable {
func toInt() -> Int {
return 42
}
}
extension Int: IntTransformable {
func toInt() -> Int {
return self
}
}
extension IntTransformable {
// throws: Protocol 'IntTransformable' can only be used as a generic constraint because it has Self or associated type requirements
static func == (lhs: IntTransformable, rhs: IntTransformable) -> Bool {
return lhs.toInt() == rhs.toInt()
}
}
// throws: Ambiguous reference to operator function '=='
if "42" == 42 {
print("equal")
} else {
print("unequal")
}
You should use these two functions:
func ==(lhs: String, rhs: #autoclosure ()->Int) -> Bool {
guard let stringIntValue = Int(lhs) else { return false }
return stringIntValue == rhs()
}
func ==(lhs: Int, rhs: String) -> Bool {
guard let stringIntValue = Int(rhs) else { return false }
return lhs == stringIntValue
}
But if you really want to involve Protocols here, you should do this like:
extension IntTransformable {
static func ==<T: IntTransformable>(lhs: Self, rhs: T) -> Bool {
return lhs.toInt() == rhs.toInt()
}
}
Usage:
print( 42 == "42" )
print( "42" == 42 )
You're way overthinking this. There is no reason to use protocols here, and you really can't do it because protocols are not really types. Just write your operator(s) at top level:
func == (lhs: Int, rhs: String) -> Bool {
return lhs == Int(rhs)
}
func == (lhs: String, rhs: Int) -> Bool {
return Int(lhs) == rhs
}
Testing:
print(5 == "5") // true
create a string extension, like
public string ToInt(this int value)
{
// some conversion code
return ConvertedStringValue;
}
or you can use a uint.TryParse(string value, out uint output)
this statement will return true if conversion is successful.
Related
Very often you have Int enums like this:
enum Difficulty: Int {
case Easy = 0
case Normal
case Hard
}
Difficulty values have a certain meaning and we may want to introduce order for them. For example, somewhere we need to compare:
let isBonusAvailable = level.difficulty.rawVAlue <= Difficulty.Hard.rawValue
I want to make this code a little bit shorter:
let isBonusAvailable = level.difficulty <= .Hard
It can be easily achieved if I add <= directly to the Difficulty. But I wanted to solve this problem in general, so I tried this super-tricky way:
protocol RawRepresentableByInt {
var rawValue: Int { get }
}
extension RawRepresentableByInt {
static func <(lhs: RawRepresentableByInt, rhs: RawRepresentableByInt) -> Bool {
return lhs.rawValue < rhs.rawValue
}
static func >(lhs: RawRepresentableByInt, rhs: RawRepresentableByInt) -> Bool {
return lhs.rawValue > rhs.rawValue
}
static func <=(lhs: RawRepresentableByInt, rhs: RawRepresentableByInt) -> Bool {
return lhs.rawValue <= rhs.rawValue
}
static func >=(lhs: RawRepresentableByInt, rhs: RawRepresentableByInt) -> Bool {
return lhs.rawValue >= rhs.rawValue
}
}
// Error: Extension of protocol 'RawRepresentable' cannot have an inheritance clause
extension RawRepresentable: RawRepresentableByInt where RawValue == Int {
}
It produces a compiler error:
Error: Extension of protocol 'RawRepresentable' cannot have an inheritance clause
I think there is nothing unimplementable in comparison of Int enum in term of logic. Please, help me to trick the Swift compiler. Anyone, who also need such extensions may participate.
It was easier than I thought. So, basically you can use Self instead of creating an additional protocol.
enum Difficulty: Int {
case Easy = 0
case Normal
case Hard
}
extension RawRepresentable where RawValue: Comparable {
static func <(lhs: Self, rhs: Self) -> Bool {
return lhs.rawValue < rhs.rawValue
}
static func >(lhs: Self, rhs: Self) -> Bool {
return lhs.rawValue > rhs.rawValue
}
static func <=(lhs: Self, rhs: Self) -> Bool {
return lhs.rawValue <= rhs.rawValue
}
static func >=(lhs: Self, rhs: Self) -> Bool {
return lhs.rawValue >= rhs.rawValue
}
}
let easy = Difficulty.Easy
print(easy > .Hard) // false
It's simple to extend a Collection type in swift to have a custom function for particular Element types:
struct MyStruct {
let string: String
}
extension Set where Element == MyStruct {
func getFirstWith(string: String) -> Element? {
return filter({ $0.string == string }).first
}
}
But suppose your Element type is generic?
protocol MyProtocol {}
struct MyGenericStruct<T: MyProtocol> {
let string: String
}
// error on next line:
// error: expected '>' to complete generic argument list
// extension Set where Element == MyGenericStruct<T: MyProtocol> {
// ^
extension Set where Element == MyGenericStruct<T: MyProtocol> {
func getFirstWith(string: String) -> Element? {
return filter({ $0.string == string }).first
}
}
// error on next line:
// error: use of undeclared type 'T'
// extension Set where Element == MyGenericStruct<T> {
// ^
extension Set where Element == MyGenericStruct<T> {
func getFirstWith(string: String) -> Element? {
return filter({ $0.string == string }).first
}
}
It's unclear to me how to declare that my element is a generic.
Details
Swift 3.1, xCode 8.3.3
Solution
import Foundation
struct MyStruct:Hashable {
let string: String
static func == (lhs: MyStruct, rhs: MyStruct) -> Bool {
return lhs.string == rhs.string
}
var hashValue: Int {
return string.hash
}
}
extension Set where Element == MyStruct {
func getFirstWith(string: String) -> Element? {
return filter({ $0.string == string }).first
}
}
var set = Set<MyStruct>()
set.insert(MyStruct(string: "123"))
set.insert(MyStruct(string: "231"))
print(String(describing:set.getFirstWith(string: "123")))
print(String(describing:set.getFirstWith(string: "231")))
///////////////////////////////////
protocol MyProtocol {}
struct MyType1: MyProtocol {}
struct MyType2: MyProtocol {}
struct MyGenericStruct<T: MyProtocol>: Hashable {
let string: String
static func == (lhs: MyGenericStruct, rhs: MyGenericStruct) -> Bool {
return lhs.string == rhs.string
}
var hashValue: Int {
return string.hash
}
}
extension Set where Element == AnyHashable {
func getFirstWith(string: String) -> Element? {
return filter{ element -> Bool in
if let _element = element as? MyGenericStruct<MyType1> {
if _element.string == string {
return true
}
}
if let _element = element as? MyGenericStruct<MyType2> {
if _element.string == string {
return true
}
}
return false
}.first
}
}
var set2 = Set<AnyHashable>()
set2.insert(MyGenericStruct<MyType1>(string: "abc"))
set2.insert(MyGenericStruct<MyType2>(string: "cba"))
print(String(describing: set2.getFirstWith(string: "abc")))
print(String(describing:set2.getFirstWith(string: "cba")))
Result
Answering my own question here on request.
I was unable to figure out how to make this an extension on Set.
Instead, we wrote a little utility function. Not as clear as a extension, but gets the work done. It looks like this:
func getFirst<T: MyProtocol>(fromSet set: Set<MyGenericStruct<T>>, whereStringIs string: String) -> MyGenericStruct<T>? {
return set.first(where: { $0.string == string })
}
As others have noted, MyGenericStruct needs to be Hashable.
If I do understand the purpose, you probably need to have more implementation in your protocol and structure.
First, element's protocol has to be hashable and equitable.
protocol MyProtocol: Hashable, Equatable {
var string: String { get }
}
Therefore, MyGenericStruct will look like the following.
struct MyGenericStruct: MyProtocol {
let string: String
var hashValue: Int {
get {
return string.hashValue
}
}
static func ==(lhs: MyGenericStruct, rhs: MyGenericStruct) -> Bool {
return lhs.hashValue == rhs.hashValue
}
}
Then, declare extension with constraints specified by a where clause for MyProtocol
extension Set where Element: MyProtocol {
func getFirstWith(string: String) -> Element? {
return filter({$0.string == string}).first
}
}
Finally, let's do a test and see its result.
// Example
let set: Set = [
MyGenericStruct(string: "Watermelon"),
MyGenericStruct(string: "Orange"),
MyGenericStruct(string: "Banana")
]
let output = set.getFirstWith(string: "Orange")
print(output)
In my playground with Xcode 8, I can get Optional(MyGenericStruct(string: "Orange")) in log.
[UPDATED1] To make an Extension on Set<MyGenericStruct> only:
extension Set where Element == MyGenericStruct {
func getFirstWith(string: String) -> Element? {
return filter({$0.string == string}).first
}
}
[UPDATED2] In case to keep MyGenericStruct<T: MyProtocol> declaration as stated is necessary, another approach to implement Set extension:
protocol MyProtocol {
}
struct BaseStruct1: MyProtocol {
}
struct BaseStruct2: MyProtocol {
}
protocol ContainStringProtocol: Hashable {
var string: String { get }
}
struct MyGenericStruct<T: MyProtocol>: ContainStringProtocol {
var string: String
var hashValue: Int {
get {
return string.hashValue
}
}
init(string: String) {
self.string = string
}
static func ==(lhs: MyGenericStruct, rhs: MyGenericStruct) -> Bool {
return lhs.hashValue == rhs.hashValue
}
}
extension Set where Element: ContainStringProtocol {
func getFirstWith(string: String) -> Element? {
return filter({$0.string == string}).first
}
}
// Example
let set1: Set = [
MyGenericStruct<BaseStruct1>(string: "Watermelon"),
MyGenericStruct<BaseStruct1>(string: "Orange"),
MyGenericStruct<BaseStruct1>(string: "Banana")
]
let output1 = set1.getFirstWith(string: "Orange")
print(output1!)
let set2: Set = [
MyGenericStruct<BaseStruct2>(string: "Watermelon"),
MyGenericStruct<BaseStruct2>(string: "Orange"),
MyGenericStruct<BaseStruct2>(string: "Banana")
]
let output2 = set2.getFirstWith(string: "Orange")
print(output2!)
Mission: I need to provide an Array extension method which would compare 2 arrays of raw representables whose raw type conforms to Equatable and say if the arrays contain the same elements by reusing the below pieces of code.
What I have at the moment:
public extension Array {
func containsTheSameElements(as array: [Element], condition: #escaping (Element, Element) -> Bool) -> Bool {
var localCopy = array
let countOfUncommonElements = reduce(0) { (uncommonElementsCount: Int, item: Element) -> Int in
if let index = localCopy.index(where: {condition(item, $0)}) {
localCopy.remove(at: index)
return uncommonElementsCount
} else {
return uncommonElementsCount + 1
}
}
return countOfUncommonElements == 0 && count == array.count
}
}
func enumComparisonClosure<T: RawRepresentable>(firstItem: T, secondItem: T) -> Bool where T.RawValue: Equatable {
return firstItem == secondItem
}
How I'm using it at the moment:
class Somewhere {
enum EnumType: String {
case first
case second
}
func method() {
let firstArray: [EnumType] = [.first, .second]
let secondArray: [EnumType] = [.second]
firstArray.containsTheSameElements(as: secondArray, condition: enumComparisonClosure)
}
}
How I would like to use it:
firstArray.containsTheSameElements(as: secondArray)
How I would like to be able to implement it:
public extension Array {
func containsTheSameElements<Element: RawRepresentable>(as array: [Element]) -> Bool where Element.RawValue: Equatable {
return containsTheSameElements(as: array, condition: enumComparisonClosure)
}
}
How can I constrain the Array's extension "Element" typealias to be a RawRepresentable with RawValue of Equatable type?
Or what would be another approach to make this comparison possible?
The constraints on the extension are separated by a comma, not
by &. The containsTheSameElements must take a [T] argument
where T is a RawRepresentable with the same RawValue
as the array elements.
Example:
extension Array where Element: RawRepresentable, Element.RawValue: Equatable {
func containsTheSameElements<T>(as array: [T]) -> Bool
where T: RawRepresentable, T.RawValue == Element.RawValue
{
return self.count == array.count &&
!zip(self, array).contains { $0.rawValue != $1.rawValue }
}
}
Update: It is simpler if you need it only for arrays
of the same type:
extension Array where Element: RawRepresentable, Element.RawValue: Equatable {
func containsTheSameElements(as array: [Element]) -> Bool
{
return self.count == array.count &&
!zip(self, array).contains { $0.rawValue != $1.rawValue }
}
}
or, reusing your existing method:
extension Array where Element: RawRepresentable, Element.RawValue: Equatable {
func containsTheSameElements(as array: [Element]) -> Bool
{
return containsTheSameElements(as: array, condition: enumComparisonClosure)
}
}
I'm trying to figure out how to use a protocol that has a Self or associated type requirement inside of another protocol. Consider the following example:
protocol SortBy {
static var values: [Self] { get }
var title: String { get }
}
protocol Filter {
var sortBy: SortBy { get set }
init(_ filter: Self)
static func == (lhs: Self, rhs: Self) -> Bool
static func != (lhs: Self, rhs: Self) -> Bool
}
I know that since SortBy contains a reference to Self, I can only use it as a generic constraint. What I don't know is if there is any Swift magic I can do to allow what I'm trying to achieve?
Ultimately I want to use the protocols for an example as follows:
enum Sort: SortBy {
case sort1
case sort2
static var values: [Sort] {
return [
.sort1,
.sort2
]
}
var title: String {
switch self {
case .sort1:
return "Sort 1"
case .sort2:
return "Sort 2"
}
}
}
struct FilterExample: Filter {
var sortBy: SortBy
init(_ filter: FilterExample) {
...
}
static func == (lhs: FilterExample, rhs: FilterExample) -> Bool {
...
}
static func != (lhs: FilterExample, rhs: FilterExample) -> Bool {
...
}
}
And in case it matters, I'm using Swift 3.
Thanks for the help in advance.
associatedTypes and generics should be able to get what you want. That said, you should take a look at the Equatable and Comparable protocols. It looks like you're trying to do something related and adoption of those may help you. E.g. adoption of Equatable means you only have to implement == and you get != for free.
protocol SortBy {
static var values: [Self] { get }
var title: String { get }
}
protocol Filter {
associatedtype SortByType: SortBy
var sortBy: SortByType { get set }
init(_ filter: Self)
static func == (lhs: Self, rhs: Self) -> Bool
static func != (lhs: Self, rhs: Self) -> Bool
}
struct FilterExample<T: SortBy>: Filter {
var sortBy: T
init(_ filter: FilterExample<T>) {
self.sortBy = filter.sortBy
}
static func == (lhs: FilterExample, rhs: FilterExample) -> Bool {
return true
}
static func != (lhs: FilterExample, rhs: FilterExample) -> Bool {
return true
}
}
I am trying to implement a simple array based heap but running into issues with the nested Node class. I would like my nodes to be comparable, however, the compiler is complaining that the node class doesn't conform.
import Foundation
class Heap<E:Comparable> {
var heap = Array<Node<E>>()
init() { }
class Node<E:Comparable >:Comparable {
var key:E!
init(key:E) { self.key = key }
}
}
func < <E> (lhs:Heap<E>.Node<E>, rhs:Heap<E>.Node<E>) -> Bool {
return lhs.key < rhs.key }
func == <E> (lhs:Heap<E>.Node<E>, rhs:Heap<E>.Node<E>) -> Bool {
return lhs.key == rhs.key }
It complains because of the Node class doesn't conform the Comparable protocol.
protocol Comparable : _Comparable, Equatable {
func <=(lhs: Self, rhs: Self) -> Bool
func >=(lhs: Self, rhs: Self) -> Bool
func >(lhs: Self, rhs: Self) -> Bool
}
And one more thing, if you want to use the self.key to compare, then the type should conform Comparable protocol as well. Here's the sample:
class Node<E where E:Comparable>:Comparable {
var key:E!
init(key:E) { self.key = key }
}
func ==<E>(lhs: Node<E>, rhs: Node<E>) -> Bool {
return lhs.key == rhs.key
}
func <<E>(lhs: Node<E>, rhs: Node<E>) -> Bool {
return lhs.key < rhs.key
}