Swift: A walk-through of operator assignments, methods and algorithms that use Equatable



Following on from earlier posts outlining the types and protocols that adopt the Equatable protocol and also the list of available operators, methods and algorithms that leverage Equatable, here is a more detailed walk-through of the operators, methods and algorithms.

Dictionary comparison

In this != operator assignment, first of all the KeyType and ValueType adopt the Equatable protocol. But it is important to note that KeyType and ValueType do not have a meaning outside of this context. They are not structs or classes in their own right, they are simply parameter names used within this declaration.
func !=<KeyType : Equatable, ValueType : Equatable>(lhs: [KeyType : ValueType], rhs: [KeyType : ValueType]) -> Bool
KeyValue and ValueType represent dictionary keys and the dictionary values, as one might guess, and they can both be instances of any type that adopts (or inherits) the Equatable protocol (for a full list see here), or subclasses of a type that adopts the Equatable protocol. An example use would be:
if ["one":2,"three":4] != ["one":"two","three":"four"] {
    println("not the same")
}
The counterpart of this operator assignment is:
func ==<KeyType : Equatable, ValueType : Equatable>(lhs: [KeyType : ValueType], rhs: [KeyType : ValueType]) -> Bool
Which is identical except for the fact that it returns true when dictionary keys and values match rather than the opposite.

Array comparison

In this pair of operator assignments, an Array is tested against another Array (the elements of both arrays must adopt the Equatable protocol). As with KeyType and ValueType, T doesn't have meaning outside this context. But here, as has been stated, it means a type that adopts the Equatable protocol.
func !=<T : Equatable>(lhs: [T], rhs: [T]) -> Bool 
func ==<T : Equatable>(lhs: [T], rhs: [T]) -> Bool
Note: lhs simply means the array to the left of the operator and rhs means the array to the right of the operator.

Slice comparison

A slice is a section of a sequence. Unlike KeyType and ValueType, Slice is a struct, a type in its own right. And these slices are made up of the generic type T. 
func !=<T : Equatable>(lhs: Slice<T>, rhs: Slice<T>) -> Bool
func ==<T : Equatable>(lhs: Slice<T>, rhs: Slice<T>) -> Bool
Instances of Slice are returned, for example, when the split() or dropFirst() algorithms are used on an Array or String. It even has its own list of methods, although these largely (if not wholly) mirror those of Array.

In the following it is true that the slices are not the same:

var a = dropLast([1,2,3])
 var b = dropLast([2,2,3])
             
 if a != b {
        println("slices different")
        }

ContiguousArray comparison

There are also operator assignments for testing the non-equality and equality of two instances of ContiguousArrays:
func !=<T : Equatable>(lhs: ContiguousArray<T>, rhs: ContiguousArray<T>) -> Bool
func ==<T : Equatable>(lhs: ContiguousArray<T>, rhs: ContiguousArray<T>) -> Bool
A contiguous array can be created like any other instance
var contigArray:ContiguousArray = [1,2,3]
and the non-equality and equality tested using != and == operators respectively.

Optionals and Type instances

Here we have the assignments that enable us to test non-equality and equality of optionals and regular instances of any type.
func !=<T : Equatable>(lhs: T?, rhs: T?) -> Bool
func !=<T : Equatable>(lhs: T, rhs: T) -> Bool

func ==<T : Equatable>(lhs: T?, rhs: T?) -> Bool
func ==<T : Equatable>(lhs: T, rhs: T) -> Bool

Contains

Things start to become a little more elaborate when we reach contains(). Here the Equatable adopter is an S.GeneratorType.Element, which on first reading appears a little overwhelming, but really it is simply the element that makes up the sequence.
/// Return `true` iff [sic] `x` is in `seq`.
func contains<S : SequenceType where S.GeneratorType.Element : Equatable>(seq: S, x: S.GeneratorType.Element) -> Bool
For example, a String is made up of Characters. So using contains() on a String (which adopts the Sequence protocol) we would use the algorithm to search for a Character:
if contains("Hello Swift!", "!") {
    println("Found it!")
}
With an Array, which also adopts Sequence, we'd write:
if contains([1,2,3,4], 2) {
    println("Found it!")
}
Here the S.GeneratorType.Element is whatever items the array is made up of. Here we use an Int array and so the elements are Ints.

Equal

The equal() algorithm allows us to compare two sequences that contain a sequence of equatable elements:
/// Return true iff [sic] `a1` and `a2` contain the same elements.
func equal<S1 : SequenceType, S2 : SequenceType where S1.GeneratorType.Element == S1.GeneratorType.Element, S1.GeneratorType.Element : Equatable>(a1: S1, a2: S2) -> Bool
It's use is intuitive.
if equal("Hello Swift!", "Hello Swift!") {
   println("Equal!")
}

Find

The find() is similar to contains() but takes a type that adopts the Collection protocol rather than Sequence and instead of returning a Bool it returns an optional that is of the Collection's index type.
func find<C : CollectionType where C.GeneratorType.Element : Equatable>(domain: C, value: C.GeneratorType.Element) -> C.IndexType?
We would incorporate this into code like this:
if let a = find("Hello Swift!", "o") {
     println("Found at index: \(a)")
}

StartsWith

I'm hoping that by this stage you are starting to get the hang of this. And so we see with the startsWith() algorithm that two sequences are used and the elements within these sequences must adopt the Equatable protocol. As indeed Character does and String itself.
/// Return true iff the the initial elements of `s` are equal to `prefix`.
func startsWith<S0 : SequenceType, S1 : SequenceType where S0.GeneratorType.Element == S0.GeneratorType.Element, S0.GeneratorType.Element : Equatable>(s: S0, prefix: S1) -> Bool
As well as using this to test a String as a prefix to a String, an Array of strings can be tested just as well. For example:
if startsWith(["Hello","Swift","Welcome","Xcode"], ["Hello","Swift"]) {
    println("Starts with")
}

Approximately equal to

Finally we arrive at the approximately equal to operator assignment:
func ~=<T : Equatable>(a: T, b: T) -> Bool
This enables us to do things like this:
if 2 ~= 1.99999999999999999 {
   println("close enough")
}
Endorse on Coderwall

Comments