Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift Set of Structure Types

Tags:

struct

swift

set

Say I have a struct, which could be anything:

struct Cube {
    var x: Int
    var y: Int
    var z: Int
    var width: Int
    // ...
}

How do I then create a Set of these points, such that there will be no two objects with the same properties?

let points: Set<Cube> = Set()
// Type ‘Cube’ does not conform to protocol ‘Hashable’

But it’s not immediately obvious how to implement hashable. From what I read, I need to make a hash function, but that doesn’t look easily possible with the amount of properties I have in the struct.

like image 535
Jacob Parker Avatar asked Aug 22 '16 12:08

Jacob Parker


People also ask

What are structures Swift?

Structures, or structs, are one of the named types in Swift that allow you to encapsulate related properties and behaviors. You can define it, give it a name and then use it in your code.

Which is better struct or class in Swift?

If you don't need inheritance, it's smarter to use structs (instead of classes). Structs can still conform to protocols, and they can be extended, so you still have a few options for creating flexible, composable code. Using structs has a huge benefit: it's easier to reason about data changes in your code.

Is string a struct or class Swift?

Swift takes a different approach: strings are structs, and in fact come packed with functionality. Almost all of Swift's core types are implemented as structs, including strings, integers, arrays, dictionaries, and even Booleans.

What is a set Swift?

Swift provides three primary collection types, known as arrays, sets, and dictionaries, for storing collections of values. Arrays are ordered collections of values. Sets are unordered collections of unique values. Dictionaries are unordered collections of key-value associations.


2 Answers

Update: As of Swift 4.1 (Xcode 9.4) the compiler can synthesize the == and hashValue methods if all members of the struct are Equatable/Hashable. Therefore is suffices to declare the conformance:

struct Cube: Hashable {
    var x: Int
    var y: Int
    var z: Int
    var width: Int
}

Previous answer for older Swift versions:

First of all, Hashable extends Equatable, so you must implement a == operator which compares two values, using all properties which uniquely identify a cube:

func ==(lhs: Cube, rhs: Cube) -> Bool {
    return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z && lhs.width == rhs.width
}

The Hashable protocol only requires that

x == y implies x.hashValue == y.hashValue

so

var hashValue: Int {
    return 0
}

would be a valid (and working) implementation. However, this would put all objects in the same hash bucket of a set (or dictionary), which is not effective. A better implementation is for example

struct Cube: Hashable {
    var x: Int
    var y: Int
    var z: Int
    var width: Int
    
    var hashValue: Int {
        return x.hashValue ^ y.hashValue ^ z.hashValue ^ width.hashValue
    }
}

Here the "XOR" operator ^ is chosen because it cannot overflow. You could also use the "overflow operator" &+.

More sophisticated hash functions would be able to distinguish different values better, so that the set operations become faster. On the other hand, the computation of the hash function itself would be slower. Therefore I would look for a "better" hash function only if the set operations turn out to be a performance bottleneck in your program.

like image 76
Martin R Avatar answered Oct 23 '22 15:10

Martin R


Implementing Hashable protocol consists of two things. First is implementing hashValue and second one is implementing equality operator.

To have working Hashable protocol important part is equality operator. It has to be implemented in a way that returns true only and only then if two structures contain same values.

On the other hand your hashValue implementation can return literally anything as long as same structure will always return same value.

The only thing that hashValue influences is how fast will your code work, because when you add or look up for values, first code that will run is hashValue. If hashValue returns same value for two structures, then equality between them will be determined by calling equality operator that would otherwise be skipped.

struct Cube: Hashable {

    // satisfy Hashable requirement
    var hashValue: Int {
        get {
            // you can return any integer here
            return x &+ y &+ z &+...
            // or even the same one for all structs
            return 0
        }
    }
}

// satisfy Equatable requirement
func ==(lhs: Cube, rhs: Cube) -> Bool {
    return lhs.x == rhs.x && lhs.y == rhs.y .....
}
like image 44
Dalija Prasnikar Avatar answered Oct 23 '22 15:10

Dalija Prasnikar