In the below code, I get an error [String : Double] does not conform to Hashable. How do i get around this?
I see the problem of non-conformance to Hashable protocol, but i'm wondering why this would be the case , that other way works. Is only 'Key' in a dictionary is required to confirm to Hashable? Some explanation would help
enum someEnumType {
case First(String, (Int, Int)->Int)
case Second (String, Int)
}
// var operations = [someEnumType : [String : Double]](); <--- This syntax Works
var operations = [[String : Double] : someEnumType ](); <--- But this does not work, ideally - i want this.
Dictionaries are also called† hash tables; they work by hashing the key. So, yes, it does need to be Hashable. The value doesn't since the point is to look up values by key.
† Well, strictly speaking one could implement a dictionary without hashing, but in practice a data structure called a dictionary in programming languages is usually understood to be a hash map. In Swift as well, the Dictionary documentation specifies it as a “hash-based mapping”.
You are correct, only a Dictionary's key must conform to the Hashable protocol.
"How do I get around this?"
Probably the most direct way of using a Dictionary the way you want to is to define your own key type. A struct makes sense here; it offers the same value semantics that your [String: Double] would-be key offers, and it is easy to define:
struct MyKey {
let myString: String
let myDouble: Double
}
Of course, it must be hashable to be used as a Dictionary key, so we add Hashable conformance:
struct MyKey: Hashable {
let myString: String
let myDouble: Double
var hashValue: Int {
return self.myString.hashValue ^ self.myDouble.hashValue
}
}
I did a cute trick there to calculate a "unique-ish" hash value for this key type: just an XOR of the string's and double's hash values. I won't guarantee uniqueness, but it should be good enough for most cases. Calculating a better hash value is an exercise I'll leave up to you if you want (but it will work just fine like this).
Finally, to conform to Hashable, one must also conform to Equatable. In our case we'll just check to see if each of a key's properties match the other's to determine if keys are equal. The full implementation:
struct MyKey: Hashable {
let myString: String
let myDouble: Double
var hashValue: Int {
return self.myString.hashValue ^ self.myDouble.hashValue
}
}
func ==(lhs: MyKey, rhs: MyKey) -> Bool {
return lhs.myString == rhs.myString && lhs.myDouble == rhs.myDouble
}
Now, you may define your Dictionary like this:
var operations = [MyKey : someEnumType ]()
And add an entry like this:
let myFirstKey = MyKey(myString: "Hello", myDouble: 1.0)
operations[myFirstKey] = someEnumType
Yes, the key needs to be hashable.
You should be able to work around this by using NSString rather than String.
Related
I am making a structure that acts like a String, except that it only deals with Unicode UTF-32 scalar values. Thus, it is an array of UInt32. (See this question for more background.)
What I want to do
I want to be able to use my custom ScalarString struct as a key in a dictionary. For example:
var suffixDictionary = [ScalarString: ScalarString]() // Unicode key, rendered glyph value
// populate dictionary
suffixDictionary[keyScalarString] = valueScalarString
// ...
// check if dictionary contains Unicode scalar string key
if let renderedSuffix = suffixDictionary[unicodeScalarString] {
// do something with value
}
Problem
In order to do that, ScalarString needs to implement the Hashable Protocol. I thought I would be able to do something like this:
struct ScalarString: Hashable {
private var scalarArray: [UInt32] = []
var hashValue : Int {
get {
return self.scalarArray.hashValue // error
}
}
}
func ==(left: ScalarString, right: ScalarString) -> Bool {
return left.hashValue == right.hashValue
}
but then I discovered that Swift arrays don't have a hashValue.
What I read
The article Strategies for Implementing the Hashable Protocol in Swift had a lot of great ideas, but I didn't see any that seemed like they would work well in this case. Specifically,
Object property (array is does not have hashValue)
ID property (not sure how this could be implemented well)
Formula (seems like any formula for a string of 32 bit integers would be processor heavy and have lots of integer overflow)
ObjectIdentifier (I'm using a struct, not a class)
Inheriting from NSObject (I'm using a struct, not a class)
Here are some other things I read:
Implementing Swift's Hashable Protocol
Swift Comparison Protocols
Perfect hash function
Membership of custom objects in Swift Arrays and Dictionaries
How to implement Hashable for your custom class
Writing a good Hashable implementation in Swift
Question
Swift Strings have a hashValue property, so I know it is possible to do.
How would I create a hashValue for my custom structure?
Updates
Update 1: I would like to do something that does not involve converting to String and then using String's hashValue. My whole point for making my own structure was so that I could avoid doing lots of String conversions. String gets it's hashValue from somewhere. It seems like I could get it using the same method.
Update 2: I've been looking into the implementation of string hash codes algorithms from other contexts. I'm having a little difficulty knowing which is best and expressing them in Swift, though.
Java hashCode algorithm
C algorithms
hash function for string (SO question and answers in C)
Hashing tutorial (Virginia Tech Algorithm Visualization Research Group)
General Purpose Hash Function Algorithms
Update 3
I would prefer not to import any external frameworks unless that is the recommended way to go for these things.
I submitted a possible solution using the DJB Hash Function.
Update
Martin R writes:
As of Swift 4.1, the compiler can synthesize Equatable and Hashable
for types conformance automatically, if all members conform to
Equatable/Hashable (SE0185). And as of Swift 4.2, a high-quality hash
combiner is built-in into the Swift standard library (SE-0206).
Therefore there is no need anymore to define your own hashing
function, it suffices to declare the conformance:
struct ScalarString: Hashable, ... {
private var scalarArray: [UInt32] = []
// ... }
Thus, the answer below needs to be rewritten (yet again). Until that happens refer to Martin R's answer from the link above.
Old Answer:
This answer has been completely rewritten after submitting my original answer to code review.
How to implement to Hashable protocol
The Hashable protocol allows you to use your custom class or struct as a dictionary key. In order to implement this protocol you need to
Implement the Equatable protocol (Hashable inherits from Equatable)
Return a computed hashValue
These points follow from the axiom given in the documentation:
x == y implies x.hashValue == y.hashValue
where x and y are values of some Type.
Implement the Equatable protocol
In order to implement the Equatable protocol, you define how your type uses the == (equivalence) operator. In your example, equivalence can be determined like this:
func ==(left: ScalarString, right: ScalarString) -> Bool {
return left.scalarArray == right.scalarArray
}
The == function is global so it goes outside of your class or struct.
Return a computed hashValue
Your custom class or struct must also have a computed hashValue variable. A good hash algorithm will provide a wide range of hash values. However, it should be noted that you do not need to guarantee that the hash values are all unique. When two different values have identical hash values, this is called a hash collision. It requires some extra work when there is a collision (which is why a good distribution is desirable), but some collisions are to be expected. As I understand it, the == function does that extra work. (Update: It looks like == may do all the work.)
There are a number of ways to calculate the hash value. For example, you could do something as simple as returning the number of elements in the array.
var hashValue: Int {
return self.scalarArray.count
}
This would give a hash collision every time two arrays had the same number of elements but different values. NSArray apparently uses this approach.
DJB Hash Function
A common hash function that works with strings is the DJB hash function. This is the one I will be using, but check out some others here.
A Swift implementation provided by #MartinR follows:
var hashValue: Int {
return self.scalarArray.reduce(5381) {
($0 << 5) &+ $0 &+ Int($1)
}
}
This is an improved version of my original implementation, but let me also include the older expanded form, which may be more readable for people not familiar with reduce. This is equivalent, I believe:
var hashValue: Int {
// DJB Hash Function
var hash = 5381
for(var i = 0; i < self.scalarArray.count; i++)
{
hash = ((hash << 5) &+ hash) &+ Int(self.scalarArray[i])
}
return hash
}
The &+ operator allows Int to overflow and start over again for long strings.
Big Picture
We have looked at the pieces, but let me now show the whole example code as it relates to the Hashable protocol. ScalarString is the custom type from the question. This will be different for different people, of course.
// Include the Hashable keyword after the class/struct name
struct ScalarString: Hashable {
private var scalarArray: [UInt32] = []
// required var for the Hashable protocol
var hashValue: Int {
// DJB hash function
return self.scalarArray.reduce(5381) {
($0 << 5) &+ $0 &+ Int($1)
}
}
}
// required function for the Equatable protocol, which Hashable inheirits from
func ==(left: ScalarString, right: ScalarString) -> Bool {
return left.scalarArray == right.scalarArray
}
Other helpful reading
Which hashing algorithm is best for uniqueness and speed?
Overflow Operators
Why are 5381 and 33 so important in the djb2 algorithm?
How are hash collisions handled?
Credits
A big thanks to Martin R over in Code Review. My rewrite is largely based on his answer. If you found this helpful, then please give him an upvote.
Update
Swift is open source now so it is possible to see how hashValue is implemented for String from the source code. It appears to be more complex than the answer I have given here, and I have not taken the time to analyze it fully. Feel free to do so yourself.
Edit (31 May '17): Please refer to the accepted answer. This answer is pretty much just a demonstration on how to use the CommonCrypto Framework
Okay, I got ahead and extended all arrays with the Hashable protocol by using the SHA-256 hashing algorithm from the CommonCrypto framework. You have to put
#import <CommonCrypto/CommonDigest.h>
into your bridging header for this to work. It's a shame that pointers have to be used though:
extension Array : Hashable, Equatable {
public var hashValue : Int {
var hash = [Int](count: Int(CC_SHA256_DIGEST_LENGTH) / sizeof(Int), repeatedValue: 0)
withUnsafeBufferPointer { ptr in
hash.withUnsafeMutableBufferPointer { (inout hPtr: UnsafeMutableBufferPointer<Int>) -> Void in
CC_SHA256(UnsafePointer<Void>(ptr.baseAddress), CC_LONG(count * sizeof(Element)), UnsafeMutablePointer<UInt8>(hPtr.baseAddress))
}
}
return hash[0]
}
}
Edit (31 May '17): Don't do this, even though SHA256 has pretty much no hash collisions, it's the wrong idea to define equality by hash equality
public func ==<T>(lhs: [T], rhs: [T]) -> Bool {
return lhs.hashValue == rhs.hashValue
}
This is as good as it gets with CommonCrypto. It's ugly, but fast and not manypretty much no hash collisions for sure
Edit (15 July '15): I just made some speed tests:
Randomly filled Int arrays of size n took on average over 1000 runs
n -> time
1000 -> 0.000037 s
10000 -> 0.000379 s
100000 -> 0.003402 s
Whereas with the string hashing method:
n -> time
1000 -> 0.001359 s
10000 -> 0.011036 s
100000 -> 0.122177 s
So the SHA-256 way is about 33 times faster than the string way. I'm not saying that using a string is a very good solution, but it's the only one we can compare it to right now
It is not a very elegant solution but it works nicely:
"\(scalarArray)".hashValue
or
scalarArray.description.hashValue
Which just uses the textual representation as a hash source
One suggestion - since you are modeling a String, would it work to convert your [UInt32] array to a String and use the String's hashValue? Like this:
var hashValue : Int {
get {
return String(self.scalarArray.map { UnicodeScalar($0) }).hashValue
}
}
That could conveniently allow you to compare your custom struct against Strings as well, though whether or not that is a good idea depends on what you are trying to do...
Note also that, using this approach, instances of ScalarString would have the same hashValue if their String representations were canonically equivalent, which may or may not be what you desire.
So I suppose that if you want the hashValue to represent a unique String, my approach would be good. If you want the hashValue to represent a unique sequence of UInt32 values, #Kametrixom's answer is the way to go...
In Tests project I've got extensions with some test helper functions. Like this:
extension Employee {
static func mockDict() -> Dictionary<String, Any>! {
return ["ID": arc4random() % 1000,
"FirstName": "Employee First Name",
...]
}
}
(I've stripped unnecessary code). I've got problem accessing ID from this dictionary for some yet unknown reason. I've got SIGABRT 6 when casting
employeeDict["ID"] as! Int
Xcode debugger console also don't like this particular integer:
Strings work fine. Have you encountered such problem? Any ideas?
EDIT: Just in case anyone will encounter this problem too. CASTING FROM UInt32/Int32 TO Int FAILS BY DESIGN. Even if object was casted into Any or Anyobject inbetween.
Even though
#available(*, message: "Converting UInt32 to Int will always succeed.")
public init?(exactly value: UInt32)
in Int's declaration
public struct Int : SignedInteger, Comparable, Equatable {
...
}
and
public struct Int32 : SignedInteger, Comparable, Equatable {
...
}
EDIT 2 for those who might encounter this behaviour in JSON serialization. Yes, serialization fails with error NSInvalidArgumentException Invalid type in JSON write (_SwiftValue) if asked to serialize UInt32, Int64 or any Integer protocol instance other than Int
Try this:
let a = employeeDict["ID"] as! UInt32
let number = Int(a)
Now you can use number to perform any action.
This works for me:
Int("\(employeeDict["ID"]!)")
Swift "primitive" numeric types are not interchangeable and cannot be cast to each other.
You need to use an initializer.
Since arcRandom() returns UInt32 and you want to use the value as Int, convert it right away in the dictionary declaration:
["ID": Int(arc4random() % 1000), ...
PS: Do not declare a clearly non-optional as implicit unwrapped optional return value, that defeats the strong type system of Swift.
static func mockDict() -> Dictionary<String, Any>
I think I have programmed myself into a corner, but I'm hoping you all know a way out. I have a class...
class Card {
var order: Int? = -1
var tag: String = "0"
var comment: String?
var data : [String: NSNumber]
}
Ideally everything would be in data, which is a few strings and lots of numbers. I started with [String, String] but I found I was writing lots of code to cast and convert when I wanted to (say) compare one of those numbers to zero. Changing it to [String, NSNumber] simplified all that code, but now my tableViewDataSource becomes very complex because some of the data is in data and some is in a separate property like comment. I even tried [String, Any], but then everything had to be cast all the time to do anything.
I have a feeling I am missing something fundamental here. When working with NSTableViews, is there a simple way to use Swift properties that I'm missing? valueForKey: does not work, there's no easy way to do a reflection-like solution I know of, etc. Any suggestions?
You can only bind dynamic properties, and your class needs to inherit from NSObject or implement NSObjectProtocol. Additionally, nilable value-types aren't allowed, so you cannot bind Int?
ie.:
class Card: NSObject {
dynamic var order: Int = -1
dynamic var tag: String = "0"
dynamic var comment: String?
dynamic var data: [String: NSNumber]
}
Consider this Person class, which simply implements StringLiteralConvertible and assigns the string literal to name:
class Person : StringLiteralConvertible {
var name : String?
typealias StringLiteralType = String
required init(stringLiteral value: StringLiteralType) {
println("stringLiteral \(value)")
name = value
}
typealias ExtendedGraphemeClusterLiteralType = String
required init(extendedGraphemeClusterLiteral value: ExtendedGraphemeClusterLiteralType) {
println("extendedGraphemeClusterLiteral \(value)")
name = value
}
typealias UnicodeScalarLiteralType = Character
required init(unicodeScalarLiteral value: UnicodeScalarLiteralType) {
println("unicodeScalarLiteral \(value)")
name = "\(value)"
}
}
This allows me to create a Person instance using a string:
let aaron : Person = "Aaron"
I can even cast an array of Persons from an array of strings:
let names = ["John", "Jane"] as [Person]
However this only works with string literals. If I use a string variable, it fails:
let aaronString = "Aaron"
let aaron : Person = aaronString
// Error: 'NSString' is not a subtype of 'Person'
Similarly, trying to cast an array of non-literal strings fails:
let nameStrings = ["John", "Jane"]
let people : [Person] = nameStrings
// Error: 'String' is not identical to 'Person'
I have three questions:
Is there another protocol I can implement to cast a non-literal string to a Person? I'd like to do this so I can cast entire collections to convert the objects.
If no to #1, is map + an initializer the best way to perform the conversion myself?
let nameStrings = ["John", "Jane"]
let people = nameStrings.map{Person(name: $0)}
If yes to #1, is there a similar approach I can use to specify an approach to convert two objects which are unrelated in hierarchy? That is, can I work around this error without an initializer?
let rikerPerson : Person = "Riker"
let rikerEmployee = rikerPerson as Employee
// Error: 'Person' is not convertible to 'Employee'
What you are describing as “casting” isn’t really casting (in the way that, say, s = “fred”; ns = s as NSString is, or that casts in C++ are).
let names = ["John", "Jane"] as [Person]
is just another a way of writing:
let names: [Person] = ["John", "Jane"]
that is, a way of telling Swift which of the many possible versions of StringLiteralConvertible to use (and not the one for String, which is the default).
Put it another way – your as is fulfilling a similar function to the as in this snippet that disambiguates two overloaded functions that differ only by return type:
func f() -> String { return "foo" }
func f() -> Int { return 42 }
let i = f() as Int // i will be 42
let s = f() as String // s will be “foo"
No “conversion” is going on here – the as is just being used to disambiguate which f Swift calls. It’s the same with which init(stringLiteral:) is chosen.
Definitely (but only if you put a space between map and the { } ;-).
If you’re concerned about the waste of converting it all to an array just to do some other thing with it, check out lazy(a).map
Nope. In the betas, there used to be a __conversion() -> T method you could implement to do “casts” like this on your own classes – or more importantly, allowed you to pass your Person class into a function that took an Employee argument and have it be converted implicitly. But that got disappeared. Generally that kind of implicit conversion is antithetical to Swift’s style, except in rare cases (Obj-C and C interop, and implicit wrapping in optionals, being the main ones). You have to write an init for Employee that takes a Person (or some class or protocol that Person conforms to), and then call it.
I understand that Swift's tuples serve, for example, as a simple way for a function to return multiple values. However, beyond this "simplicity aspect", I don't see very well any necessity of using tuples instead of structs.
Therefore, my question: in terms of design, is there any scenario where tuples are clearly a better choice than structs?
This question of a slightly "discussion" nature, but I'll add two points in favour of sometimes preferring tuples over structures.
Native Equatable conformance for limited sized tuples
In Swift 2.2, tuples of up to size 6 will be natively equatable, given that it's members are equatable
Proposal SE-0015: Tuple comparison operators
This means tuples will sometimes be the natural choice over using smaller constructs in a limited scope.
E.g. consider the following example, using (1): a structure
struct Foo {
var a : Int = 1
var b : Double = 2.0
var c : String = "3"
}
var a = Foo()
var b = Foo()
// a == b // error, Foo not Equatable
/* we can naturally fix this by conforming Foo to Equatable,
but this needs a custom fix and is not as versatile as just
using a tuple instead. For some situations, the latter will
suffice, and is to prefer. */
func == (lhs: Foo, rhs: Foo) -> Bool {
return lhs.a == rhs.a && lhs.b == rhs.b && lhs.c == rhs.c
}
and (2): a tuple
/* This will be native in Swift 2.2 */
#warn_unused_result
public func == <A: Equatable, B: Equatable, C: Equatable>(lhs: (A,B,C), rhs: (A,B,C)) -> Bool {
return lhs.0 == rhs.0 && lhs.1 == rhs.1 && lhs.2 == rhs.2
}
/* end of native part ... */
var aa = (1, 2.0, "3")
var bb = (1, 2.0, "3")
aa == bb // true
aa.0 = 2
aa == bb // false
Generic access to different type tuples: more versatile than for different type structures
From the above (compare the == functions) it's also apparent that tuples are easily to work with in the context of generics, as we can access their anonymous member properties using the .0, .1 ... suffixes; whereas for a struct, the easiest way to mimic this behaviour quickly becomes quite complex, needing tools such as runtime introspection and so on, see e.g. this.
I don't know if anyone is still interested in this but sometimes i use tuples like this to keep things organised and it seems more readable to me.
struct SystemState{
let someSetting: Int
let someChoice: Int
let howmuchDoYouNeedForAnExample: String
}
struct UserState{
let name: String
let email: String
let accessLevel: Int
let blablabla:[String:String]
}
Given the above might be defined somewhere else:
typealias State = (system: SystemState,user: UserState)
let theSystemState = SystemState(someSetting: v1, someChoice: v2,
howmuchDoYouNeedForAnExample: v3)
let theUserState = theSystemState,UserState(name: v4, email: v5,
accessLevel: v6, blablabla: v7)
var state: State = (theSystemState,theUserState)
members are now accessed like this. It looks just the same as structs.
state.system.someSetting
state.user.accessLevel
As far as i can tell there is no real difference between using structs or tuples just the advantages mentioned in the accepted answer by dfri. Tuples also offer simplicity when you just want pass around something that is easier to keep together but has no need, or conceptually makes no sense, to be encapsulated in a struct. This way your code documents your intention in some small way that might make it easier to understand in the future for you or someone else.
Consider this, java to objective-C.
When you have to plug yourself to this type of datasource in your project (because you have a huge basecode in android and don't want or cannot do everything from scratch), you find yourself with java types datasource (like hashmap for example), those are basically typedef from objective-c types.
This is not easily plugable in swift depending on what you got, if you want to have a nice array for your collectionview, an array of tuples which will be filled by java datasource can be nice.
A little sample of code to illustrate this :
var myJavaVar = JavaUtilLinkedHashMap()
var headerArray : [(varName : String, param : Int, varId : jlong)] = []
myJavaVar = myStaticMethodToGetMyVar
// get var array
let keySetJava = myJavaVar.keySet().toArray()
for jCounter in 0..<Int(myJavaVar.size()){
// id for index
let key : Int = keySetJava.objectAtIndex(UInt(jCounter)) as! Int
let varId = jlong.init(key)
let varName = myMethodToGetName(varId)
let myParam : Int = myJavaVar.getWithId(keySetJava.objectAtIndex(UInt(jCounter))) as! Int
// append data to the array
headerArray.append((varName: categoryName, param : myParam,duration: duration, varId: varId))
}
You can then get your data like this (in your collectionview method):
let time = headerArray[indexPath.section].param