Currently I have a class of generic type, and I want to make the object of this class searchable via
contains()
method for an array of those objects, by making the class conform to Hashable protocol and provide a hash value for each object. Now my problem is I have objects with exactly the same properties, and it seems that the array cannot really distinguish them (my current approach is to use one of the properties' hash value as the hash value for the class, and the
== <T> (lhs: ClassA<T>, rhs: ClassA<T>) -> Bool
function is done by comparing the hash value). I have tried to use a static property like "id", but for generic types static properties are not supported.
How should I define the hash value such that different objects with the same properties can still be differentiated?
EDIT: I'm making it conform to Hashable directly because it's also used as keys in dict in other parts of the program, since Hashable already conforms to Equatable.
My current approach is to use one of the properties' hash value as the
hash value for the class, and the
== <T> (lhs: ClassA<T>, rhs: ClassA<T>) -> Bool
function is done by comparing the hash value
That's not how the == and hashValue relationship works – don't do this. What if you get a hash collision? Two different instances with different properties could compare equal.
You should instead implement == to actually compare the properties of two instances. == should return true if two given instances have equivalent properties. The hashValues of two instances should be equivalent if they compare equal with ==.
Now, it might well be the case that you cannot do this comparison unless T is Equatable. One solution to this is to not conform ClassA to Equatable, but instead just overload == for when T is Equatable, such as:
func == <T : Equatable>(lhs: ClassA<T>, rhs: ClassA<T>) -> Bool {
// stub: do comparison logic
}
You can now just use Sequence's contains(where:) method in conjunction with the == overload in order to check if a given instance is in the array:
var array = [ClassA("foo")] // assuming ClassA has an init(_: T) and a suitable ==
// implementation to compare that value
let someInstanceToFind = ClassA("foo")
print(array.contains { $0 == someInstanceToFind }) // true
And if you want ClassA to have a hashValue, then simply write an extension that defines a hashValue when T is Hashable:
extension ClassA where T : Hashable {
var hashValue: Int {
return 0 // to do: implement hashValue logic
}
}
Unfortunately, this does mean that ClassA won't explicitly conform to Hashable when T does – but it will have a hashValue and == implementation. SE-0143: Conditional conformances will change this by allowing explicit conformance to protocols if a given where clause if satisfied, but this is yet to be implemented.
If you need explicit conformance to Hashable (such as for using instances of your class in a Set or as Dictionary keys) – then one solution is to create a wrapper type:
struct HashableClassA<T : Hashable> : Hashable {
var base: ClassA<T>
init(_ base: ClassA<T>) {
self.base = base
}
static func ==(lhs: HashableClassA, rhs: HashableClassA) -> Bool {
return lhs.base == rhs.base
}
var hashValue: Int {
return base.hashValue
}
}
Now you just have to wrap ClassA<T> instances in a HashableClassA instance before adding to a Set or Dictionary.
Just realized there is a simple way for achieving the Equatable in
contains()
method: use
return lhs === rhs
in the == function such that objects are compared directly. It's working in this way now.
Related
I'm writing a component for caching instances of classes. The classes are not per se Comparable, Hashable or Equatable. If they were, the semantics of the respective operations would not necessarily serve our purposes, so let's pretent we can not use those protocols.
Objects can be cached w.r.t. multiple keys. So when asking the cache for a list of all cached objects, I need to remove duplicates from the value set of the underlying dictionary -- with respect to object identity.
Obviously, this does the job:
var result: [C] = []
for c in dict.values {
if !result.contains(where: { (rc: C) in rc === c }) {
result.append(c)
}
}
return result
However, this has quadratic runtime behaviour. Compared to linearithmic or expected linear behaviour that are easy to get when using abovementioned protocols (using set implementations), this is bad.
So how can we efficiently remove duplicates w.r.t. object identity from a Swift collection?
We can wrap our objects into something that is Hashable and Comparable:
struct ClassWrap<T: AnyObject>: Hashable, Comparable {
var value: T
var hashValue: Int {
return ObjectIdentifier(self.value).hashValue
}
static func ==(lhs: ClassWrap, rhs: ClassWrap) -> Bool {
return lhs.value === rhs.value
}
static func <(lhs: ClassWrap<T>, rhs: ClassWrap<T>) -> Bool {
return ObjectIdentifier(lhs.value) < ObjectIdentifier(rhs.value)
}
}
Now, any regular Set implementation or otherwise unique-fying operation should do the job.
I'm aware of Swift's higher-order functions like Map, Filter, Reduce and FlatMap, but I'm not aware of any like 'All' or 'Any' which return a boolean that short-circuit on a positive test while enumerating the results.
For instance, consider you having a collection of 10,000 objects, each with a property called isFulfilled and you want to see if any in that collection have isFulfilled set to false. In C#, you could use myObjects.Any(obj -> !obj.isFulfilled) and when that condition was hit, it would short-circuit the rest of the enumeration and immediately return true.
Is there any such thing in Swift?
Sequence (and in particular Collection and Array) has a (short-circuiting) contains(where:) method taking a boolean predicate as argument. For example,
if array.contains(where: { $0 % 2 == 0 })
checks if the array contains any even number.
There is no "all" method, but you can use contains() as well
by negating both the predicate and the result. For example,
if !array.contains(where: { $0 % 2 != 0 })
checks if all numbers in the array are even. Of course you can define a custom extension method:
extension Sequence {
func allSatisfy(_ predicate: (Iterator.Element) -> Bool) -> Bool {
return !contains(where: { !predicate($0) } )
}
}
If you want to allow "throwing" predicates in the same way as the
contains method then it would be defined as
extension Sequence {
func allSatisfy(_ predicate: (Iterator.Element) throws -> Bool) rethrows -> Bool {
return try !contains(where: { try !predicate($0) } )
}
}
Update: As James Shapiro correctly noticed, an allSatisfy method has been added to the Sequence type in Swift 4.2 (currently in beta), see
SE-0027 Add an allSatisfy algorithm to Sequence
(Requires a recent 4.2 developer snapshot.)
One other thing that you can do in Swift that is similar to "short circuiting" in this case is to use the lazy property of a collection, which would change your implementation to something like this:
myObjects.lazy.filter({ !$0.isFulfilled }).first != nil
It's not exactly the same thing you're asking for, but might help provide another option when dealing with these higher-order functions. You can read more about lazy in Apple's docs. As of this edit the docs contain the following:
var lazy: LazyCollection> A view onto this collection
that provides lazy implementations of normally eager operations, such
as map and filter.
var lazy: LazySequence> A sequence containing the same
elements as this sequence, but on which some operations, such as map
and filter, are implemented lazily.
If you had all the objects in that array, they should conform to some protocol, which implements the variable isFulfilled... as you can see, you could make these objects confrom to (let's call it fulFilled protocol)... Now you can cast that array into type [FulfilledItem]... Now you can continue as usually
I am pasting code here for your better understanding:
You see, you cannot extend Any or AnyObject, because AnyObject is protocol and cannot be extended (intended by Apple I guess), but you can ,,sublass" the protocol or as you like to call it professionally - Make protocol inheriting from AnyObject...
protocol FulfilledItem: AnyObject{
var isFulfilled: Bool {get set}
}
class itemWithTrueValue: FulfilledItem{
var isFulfilled: Bool = true
}
class itemWithFalseValue: FulfilledItem{
var isFulfilled: Bool = false
}
var arrayOfFulFilled: [FulfilledItem] = [itemWithFalseValue(),itemWithFalseValue(),itemWithFalseValue(),itemWithFalseValue(),itemWithFalseValue(),itemWithFalseValue()]
let boolValue = arrayOfFulFilled.contains(where: {
$0.isFulfilled == false
})
Now we've got ourselves a pretty nice looking custom protocol inheriting all Any properties + our beautiful isFulfilled property, which we will handle now as usually...
According to apple docs:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TypeCasting.html#//apple_ref/doc/uid/TP40014097-CH22-ID342
AnyObject is only for reference types (classes), Any is for both value and reference types, so I guess it is prefered to inherit AnyObject...
Now you cast instead AnyObject into Array the protocol Item FulfilledItem and you will have beautiful solution (don't forget every item to conform to that protocol and set the value...)
Wish happy coding :)
To be very frank, I am totally new to learn Extension creation and usage.
I wanted to create a category (Extension in swift 3.0) which can be used throughout an application to perform repeated operations for Array.
Sample Link 1
This is what I have seen and understand while doing research, I wanted to create an extension with various methods which should be generic, and not on the basis of datatype needed to create separate extensions.
Here in above example, we will need to create single extension if we will go for particular datatype wise extension. I wanted to have a guidance if any way is there to create the generic category (Extension in swift).
extension _ArrayType where Generator.Element == Int
extension Array where Element: Equatable
extension Array where Element == Int
extension _ArrayType where Generator.Element == Float
extension SequenceType where Self.Generator.Element: FloatingPointType
extension Array where Element: DoubleValue
extension Sequence where Iterator.Element == String
,etc...
Sample Link 2
Note : In short, we can consider that I want to perform actions based on Array in single extension instead of just creating the single extension for each of the datatypes as per above requirement.
As mentioned in the comments, one way to accomplish this is to create your own protocol that the types you want to cover adopt (in the comments someone called it Content, used below for this example) (from first source):
protocol Content {
var hash: String { get }
}
extension Array where Element : Content {
func filterWithId(id : String) -> [Element] {
return self.filter { (item) -> Bool in
return item.id == id
}
}
}
It seems, though, that the original question is mainly asking about generic extensions for arrays, which one comment says are not possible but 100% are possible in Swift (it's a big Swift feature, actually) (from second source).
For example, if you want to define a specific extension method for Ints only, you can do that:
extension Sequence where Iterator.Element == Int {
var sum: Int {
return reduce(0, +)
}
}
It seems like the question's original requirements are extension methods that could be agnostic to data type and therefore should be kept in common. If I understand correctly, seems though that these data types in general have some conformance to Equatable and/or Hashable, which is the minimum requirement for this kind of generic-stuff to work. With this element conformance, though, this is possible as such:
extension Sequence where Iterator.Element is Equatable {
func extensionMethodName<T: Equatable>(_ input: [T], singleElement: T) -> [T] {
// T is now a generic array of equatable items. You can implement whatever extension logic you need with these.
// I added different ways of passing in and returning this generic type, but the only thing that is likely going to be consistent is the `<T: Equatable>` which is Swift standard syntax for declaring generic type parameters for a method.
}
}
The Swift syntax changes quickly, and what's here can quickly go out of date, but this guide is kept fairly up-to-date by Apple and shows the most up to date syntax for Generics used above ^.
My answer pulls from a couple StackOverflow questions/answers, used for example/syntax above ^. Source: (SO Source) (SO Source 2)
In summary, all the methods above can be combined, for a fully custom extension solution that has both generic functions/vars for all your Array types, while still having type-specific extension overrides.
In where clause, you specify "If the Element type has these rules, consider this extension".
You don't need to implement all of the methods in all extensions.
For example:
You want to extend Array<Element> to generally have method foo(_:Element):
extension Array {
func foo(bar: Element) { /*your code goes here */ }
}
You want to extend Array<Element> where Element did implement Equatable (which includes Int,Double and ... or any structs/classes you've marked as Equatable):
extension Array where Element: Equatable {
func find(value: Element) -> Bool {
return index(of: value) != nil
}
}
You want to extend Sequence in cases that Element is Numeric, have get-only variable sum:
extension Sequence where Element: Numeric {
var sum: Element {
return reduce(0, +)
}
}
You want to extend Collection<Collection<Element: Equatable>> to have a method to compare to 2D Collections:
extension Collection
where Iterator.Element: Collection,
Iterator.Element.Iterator.Element: Equatable {
func compare(to: Self) -> Bool {
let flattenSelf = self.reduce([], +)
let flattenTo = to.reduce([], +)
return flattenSelf.count == flattenTo.count &&
zip(flattenSelf, flattenTo).reduce(true) { $0 && $1.0 == $1.1 }
}
}
You don't need to extend Array or collection to have methods like sort, find, etc... Most of these methods are already extended inside the compiler if your Element: Equatable or Element: Comparable. using map, filter and reduce you can achieve more complex structures with not much of a code.
I'm experimenting with using Composition instead of Inheritance and I wanted to use diff on an array of objects that comply with a given protocol.
To do so, I implemented a protocol and made it comply with Equatable:
// Playground - noun: a place where people can play
import XCPlayground
import Foundation
protocol Field:Equatable {
var content: String { get }
}
func ==<T: Field>(lhs: T, rhs: T) -> Bool {
return lhs.content == rhs.content
}
func ==<T: Field, U: Field>(lhs: T, rhs: U) -> Bool {
return lhs.content == rhs.content
}
struct First:Field {
let content:String
}
struct Second:Field {
let content:String
}
let items:[Field] = [First(content: "abc"), Second(content: "cxz")] // 💥 boom
But I've soon discovered that:
error: protocol 'Field' can only be used as a generic constraint because it has Self or associated type requirements
I understand why since Swift is a type-safe language that needs to be able to know the concrete type of these objects at anytime.
After tinkering around, I ended up removing Equatable from the protocol and overloading the == operator:
// Playground - noun: a place where people can play
import XCPlayground
import Foundation
protocol Field {
var content: String { get }
}
func ==(lhs: Field, rhs: Field) -> Bool {
return lhs.content == rhs.content
}
func ==(lhs: [Field], rhs: [Field]) -> Bool {
return (lhs.count == rhs.count) && (zip(lhs, rhs).map(==).reduce(true, { $0 && $1 })) // naive, but let's go with it for the sake of the argument
}
struct First:Field {
let content:String
}
struct Second:Field {
let content:String
}
// Requirement #1: direct object comparison
print(First(content: "abc") == First(content: "abc")) // true
print(First(content: "abc") == Second(content: "abc")) // false
// Requirement #2: being able to diff an array of objects complying with the Field protocol
let array1:[Field] = [First(content: "abc"), Second(content: "abc")]
let array2:[Field] = [Second(content: "abc")]
print(array1 == array2) // false
let outcome = array1.diff(array2) // 💥 boom
error: value of type '[Field]' has no member 'diff'
From here on, I'm a bit lost to be honest. I read some great posts about type erasure but even the provided examples suffered from the same issue (which I assume is the lack of conformance to Equatable).
Am I right? And if so, how can this be done?
UPDATE:
I had to stop this experiment for a while and totally forgot about a dependency, sorry! Diff is a method provided by SwiftLCS, an implementation of the longest common subsequence (LCS) algorithm.
TL;DR:
The Field protocol needs to comply with Equatable but so far I have not been able to do this. I need to be able to create an array of objects that comply to this protocol (see the error in the first code block).
Thanks again
The problem comes from a combination of the meaning of the Equatable protocol and Swift’s support for type overloaded functions.
Let’s take a look at the Equatable protocol:
protocol Equatable
{
static func ==(Self, Self) -> Bool
}
What does this mean? Well it’s important to understand what “equatable” actually means in the context of Swift. “Equatable” is a trait of a structure or class that make it so that any instance of that structure or class can be compared for equality with any other instance of that structure or class. It says nothing about comparing it for equality with an instance of a different class or structure.
Think about it. Int and String are both types that are Equatable. 13 == 13 and "meredith" == "meredith". But does 13 == "meredith"?
The Equatable protocol only cares about when both things to be compared are of the same type. It says nothing about what happens when the two things are of different types. That’s why both arguments in the definition of ==(::) are of type Self.
Let’s look at what happened in your example.
protocol Field:Equatable
{
var content:String { get }
}
func ==<T:Field>(lhs:T, rhs:T) -> Bool
{
return lhs.content == rhs.content
}
func ==<T:Field, U:Field>(lhs:T, rhs:U) -> Bool
{
return lhs.content == rhs.content
}
You provided two overloads for the == operator. But only the first one has to do with Equatable conformance. The second overload is the one that gets applied when you do
First(content: "abc") == Second(content: "abc")
which has nothing to do with the Equatable protocol.
Here’s a point of confusion. Equability across instances of the same type is a lower requirement than equability across instances of different types when we’re talking about individually bound instances of types you want to test for equality. (Since we can assume both things being tested are of the same type.)
However, when we make an array of things that conform to Equatable, this is a higher requirement than making an array of things that can be tested for equality, since what you are saying is that every item in the array can be compared as if they were both of the same type. But since your structs are of different types, you can’t guarantee this, and so the code fails to compile.
Here’s another way to think of it.
Protocols without associated type requirements, and protocols with associated type requirements are really two different animals. Protocols without Self basically look and behave like types. Protocols with Self are traits that types themselves conform to. In essence, they go “up a level”, like a type of type. (Related in concept to metatypes.)
That’s why it makes no sense to write something like this:
let array:[Equatable] = [5, "a", false]
You can write this:
let array:[Int] = [5, 6, 7]
or this:
let array:[String] = ["a", "b", "c"]
or this:
let array:[Bool] = [false, true, false]
Because Int, String, and Bool are types. Equatable isn’t a type, it’s a type of a type.
It would make “sense” to write something like this…
let array:[Equatable] = [Int.self, String.self, Bool.self]
though this is really stretching the bounds of type-safe programming and so Swift doesn’t allow this. You’d need a fully flexible metatyping system like Python’s to express an idea like that.
So how do we solve your problem? Well, first of all realize that the only reason it makes sense to apply SwiftLCS on your array is because, at some level, all of your array elements can be reduced to an array of keys that are all of the same Equatable type. In this case, it’s String, since you can get an array keys:[String] by doing [Field](...).map{ $0.content }. Perhaps if we redesigned SwiftLCS, this would make a better interface for it.
However, since we can only compare our array of Fields directly, we need to make sure they can all be upcast to the same type, and the way to do that is with inheritance.
class Field:Equatable
{
let content:String
static func == (lhs:Field, rhs:Field) -> Bool
{
return lhs.content == rhs.content
}
init(_ content:String)
{
self.content = content
}
}
class First:Field
{
init(content:String)
{
super.init(content)
}
}
class Second:Field
{
init(content:String)
{
super.init(content)
}
}
let items:[Field] = [First(content: "abc"), Second(content: "cxz")]
The array then upcasts them all to type Field which is Equatable.
By the way, ironically, the “protocol-oriented” solution to this problem actually still involves inheritance. The SwiftLCS API would provide a protocol like
protocol LCSElement
{
associatedtype Key:Equatable
var key:Key { get }
}
We would specialize it with a superclass
class Field:LCSElement
{
let key:String // <- this is what specializes Key to a concrete type
static func == (lhs:Field, rhs:Field) -> Bool
{
return lhs.key == rhs.key
}
init(_ key:String)
{
self.key = key
}
}
and the library would use it as
func LCS<T: LCSElement>(array:[T])
{
array[0].key == array[1].key
...
}
Protocols and Inheritance are not opposites or substitutes for one another. They complement each other.
I know this is probably now what you want but the only way I know how to make it work is to introduce additional wrapper class:
struct FieldEquatableWrapper: Equatable {
let wrapped: Field
public static func ==(lhs: FieldEquatableWrapper, rhs: FieldEquatableWrapper) -> Bool {
return lhs.wrapped.content == rhs.wrapped.content
}
public static func diff(_ coll: [Field], _ otherCollection: [Field]) -> Diff<Int> {
let w1 = coll.map({ FieldEquatableWrapper(wrapped: $0) })
let w2 = otherCollection.map({ FieldEquatableWrapper(wrapped: $0) })
return w1.diff(w2)
}
}
and then you can do
let outcome = FieldEquatableWrapper.diff(array1, array2)
I don't think you can make Field to conform to Equatable at all as it is designed to be "type-safe" using Self pseudo-class. And this is one reason for the wrapper class. Unfortunately there seems to be one more issue that I don't know how to fix: I can't put this "wrapped" diff into Collection or Array extension and still make it support heterogenous [Field] array without compilation error:
using 'Field' as a concrete type conforming to protocol 'Field' is not supported
If anyone knows a better solution, I'm interested as well.
P.S.
In the question you mention that
print(First(content: "abc") == Second(content: "abc")) // false
but I expect that to be true given the way you defined your == operator
The Hashable protocol in Swift requires you to implement a property called hashValue:
protocol Hashable : Equatable {
/// Returns the hash value. The hash value is not guaranteed to be stable
/// across different invocations of the same program. Do not persist the hash
/// value across program runs.
///
/// The value of `hashValue` property must be consistent with the equality
/// comparison: if two values compare equal, they must have equal hash
/// values.
var hashValue: Int { get }
}
However, it seems there's also a similar property called hash.
What is the difference between hash and hashValue?
hash is a required property in the NSObject protocol, which groups methods that are fundamental to all Objective-C objects, so that predates Swift.
The default implementation just returns the objects address,
as one can see in
NSObject.mm, but one can override the property
in NSObject subclasses.
hashValue is a required property of the Swift Hashable protocol.
Both are connected via a NSObject extension defined in the
Swift standard library in
ObjectiveC.swift:
extension NSObject : Equatable, Hashable {
/// The hash value.
///
/// **Axiom:** `x == y` implies `x.hashValue == y.hashValue`
///
/// - Note: the hash value is not guaranteed to be stable across
/// different invocations of the same program. Do not persist the
/// hash value across program runs.
open var hashValue: Int {
return hash
}
}
public func == (lhs: NSObject, rhs: NSObject) -> Bool {
return lhs.isEqual(rhs)
}
(For the meaning of open var, see What is the 'open' keyword in Swift?.)
So NSObject (and all subclasses) conform to the Hashable
protocol, and the default hashValue implementation
return the hash property of the object.
A similar relationship exists between the isEqual method of the
NSObject protocol, and the == operator from the Equatable
protocol: NSObject (and all subclasses) conform to the Equatable
protocol, and the default == implementation
calls the isEqual: method on the operands.