Updated 24 July 2020
Hi guys, in this session we learn about Hashable, Equatable and Comparable. Swift 4.1, the first minor release of Swift 4 which brings some useful improvements such as automatically synthesized equatable and hashable, conditional conformances, a smarter way to detect the simulator environment, and more.
A type that provides an integer hash value.
The Hashable
protocol in a set or as a dictionary key. Many types of the standard library conform to Hashable
: strings, integers, floating-point and Boolean values, and even sets provide a hash value by default. When defining an enumeration without associated values, it gains Hashable
conformance automatically, and can add Hashable
conformance to the other custom types by adding a single hashValue
property.
A hash value, provided by a type’s hashValue
property, is an integer that is the same for any two instances that compare equally. That is, for two instances a
and b
of the same type, if a == b
then a.hashValue == b.hashValue
. The reverse is not true: Two instances with equal hash values are not necessarily equal to each other.
Note: Hash values cannot guaranteed to be equal across different executions of your program. Do not save hash values to use during a future execution.
As an example, consider a GridPoint
type that describes a location in a grid of buttons. Here’s the initial declaration of the GridPoint
type:
1 2 3 4 5 |
/// A point in an x-y coordinate system. struct GridPoint { var x: Int var y: Int } |
You’d like to create a set of the grid points where a user has already tapped. Because the GridPoint
type is not hashable yet, it can’t be used in a set. To add Hashable
conformance, provide an ==
operator function and implement the hash(into:)
method.
1 2 3 4 5 6 7 8 9 10 |
extension GridPoint: Hashable { static func == (lhs: GridPoint, rhs: GridPoint) -> Bool { return lhs.x == rhs.x && lhs.y == rhs.y } func hash(into hasher: inout Hasher) { hasher.combine(x) hasher.combine(y) } } |
The hash(into:)
method in this example feeds the grid point’s x
and y
properties into the provided hasher. These properties are the same ones used to test for equality in the ==
operator function.
Now that GridPoint
conforms to the Hashable
protocol, you can create a set of previously tapped grid points.
1 2 3 4 5 6 7 8 9 |
var tappedPoints: Set = [GridPoint(x: 2, y: 3), GridPoint(x: 4, y: 1)] let nextTap = GridPoint(x: 0, y: 1) if tappedPoints.contains(nextTap) { print("Already tapped at (\(nextTap.x), \(nextTap.y)).") } else { tappedPoints.insert(nextTap) print("New tap detected at (\(nextTap.x), \(nextTap.y)).") } // Prints "New tap detected at (0, 1).") |
A type that compared for value equality.
The Equatable
protocol is what allows two objects to be compared using ==
, and it’s surprisingly easy to implement because Swift does most of the work for you by default.
Adding Equatable
conformance to your custom types means that you can use more convenient APIs when searching for particular instances in a collection. Equatable
is also the base protocol for the Hashable
and Comparable
protocols, which allow more uses of your custom type, such as constructing sets or sorting the elements of a collection.
1 2 3 4 |
struct Person { var name: String var age: String } |
To make that Equatable
you need to add the Equatable
conformance. If don’t want to check all properties for equality, or if any of your properties are not also Equatable
, then you need to write your own ==
function like this:
1 2 3 4 5 |
extension Person: Equatable { static func ==(lhs: Person, rhs: Person) -> Bool { return lhs.name == rhs.name && lhs.age == rhs.age } } |
Now use ==
to check for equality between any two instances or call the Equatable
-constrained contains(_:)
method.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
let allPerson = [person1,person2,person3,person4] let searchedPerson = Person(name: "Deepak Rawat", age: "26") if allPerson[0] == searchedPerson { print(false) } else if allPerson[2] == searchedPerson { print(true) } if allPerson.contains(searchedPerson) { print(true) } // Print: true true |
A type that can be compared using the relational operators <
, <=
, >=
, and >
.
The Comparable
protocol is used for types that have an inherent order, such as numbers and strings. Many types in the standard library already conform to the Comparable
protocol. Add Comparable
conformance to your own custom types when you want to be able to compare instances using relational operators or use standard library methods that are designed for Comparable
types.
Types with Comparable conformance implement the less-than operator (<
) and the equal-to operator (==
). These two operations impose a strict total order on the values of a type, in which exactly one of the following must be true for any two values a
and b
:
a == b
a < b
b < a
To add Comparable
conformance to your custom types, define the <
and ==
operators as static methods of your types. The ==
operator is a requirement of the Equatable
protocol, which Comparable
extends—see that protocol’s documentation for more information about equality in Swift. Because default implementations of the remainder of the relational operators are provided by the standard library, you’ll be able to use !=
, >
, <=
, and >=
with instances of your type without any further code.
Let discuss with the following example:
Stores the year, month, and day of a date in the structure.
1 2 3 4 5 |
struct DateOfBirth { let year: Int let month: Int let day: Int } |
To add Comparable
conformance to DateOfBirth, firstly declare conformance to Comparable
and implement the <
operator function.
1 2 3 4 5 6 7 8 9 10 11 |
extension DateOfBirth: Comparable { static func < (lhs: Date, rhs: Date) -> Bool { if lhs.year != rhs.year { return lhs.year < rhs.year } else if lhs.month != rhs.month { return lhs.month < rhs.month } else { return lhs.day < rhs.day } } } |
This function uses the least specific nonmatching property of the date to determine the result of the comparison. For example, if the two year
properties are equal but the two month
properties are not, the date with the lesser value for month
is the lesser of the two dates.
Next, implement the ==
operator function, the requirement inherited from the Equatable
protocol.
1 2 3 4 5 |
static func == (lhs: Date, rhs: Date) -> Bool { return lhs.year == rhs.year && lhs.month == rhs.month && lhs.day == rhs.day } } |
Two DateOfBirth instances are equal if each of their corresponding properties is equal. Now add the date of birth of Siddhant and Vicky. So we will get data that who is elder between them.
1 2 3 4 5 6 7 8 |
let siddhantDOB = DateOfBirth(year: 1996, month: 9, day: 11) // July 11, 1969 let vickyDOB = DateOfBirth(year: 1996, month: 11, day: 20) // July 20, 1969 if siddhantDOB > vickyDOB { print("Vicky is elder than Siddhant.") } else { print("Siddhant is elder than Vicky.") } // Prints "Siddhant is elder than Vicky." |
So if you have any comments, questions, or recommendations, feel free to post them in the comment section below!
If you have more details or questions, you can reply to the received confirmation email.
Back to Home
Be the first to comment.