1

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.

1
  • If you want two different objects with equal properties not to be equal, you can add an identifier (e.g. autoincrementing id for new objects) to distuinguish them or just use their pointer value (for class types). Commented Jan 18, 2017 at 20:03

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

Comments

0

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.

1 Comment

Note that this may lead to surprising results with two different instances which look like they should be equivalent – but aren't because they aren't the same instance.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.