# Defining a custom hash function

**URL:** <https://discourse.julialang.org/t/defining-a-custom-hash-function/73395>\
**Category:** General Usage\
**Created:** [December 20, 2021, 7:24pm UTC](https://discourse.julialang.org/t/defining-a-custom-hash-function/73395 "2021-12-20T19:24:26Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://discourse.julialang.org/u/jules)\
**Post date:** [December 20, 2021, 7:24pm UTC](https://discourse.julialang.org/t/defining-a-custom-hash-function/73395/1 "2021-12-20T19:24:26Z")

</div>

I have a type `BezierPath` that contains a vector of commands. I want two bezier paths with the same commands stored in different vectors to have the same hash value, by default they don’t. Is it fine to just overload `hash` to operate on the vector content? I don’t see how that could have negative consequences other than that it’s not possible anymore to use two different instances as separate keys in a dict if they have the same commands. But that’s kind of the point.

---

<div class="post-metadata">

**Author:** ![jzr](https://avatars.discourse-cdn.com/v4/letter/j/eb9ed0/32.png) [@jzr](https://discourse.julialang.org/u/jzr)\
**Post date:** [December 20, 2021, 8:08pm UTC](https://discourse.julialang.org/t/defining-a-custom-hash-function/73395/2 "2021-12-20T20:08:32Z")

</div>

> [@jules](#):
>
> it’s not possible anymore to use two different instances as separate keys in a dict if they have the same commands

Is that true? I would think they can be separate keys because equality should still be checked for lookup.

```julia
julia> mutable struct S end

julia> a = S(); b = S(); a === b
false

julia> Set([a,b])
Set{S} with 2 elements:
  S()
  S()

julia> Base.hash(::S)::UInt = 1

julia> Set([a,b])
Set{S} with 2 elements:
  S()
  S()

```

---

<div class="post-metadata">

**Author:** ![cjdoris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cjdoris/32/213133_2.png) [@cjdoris](https://discourse.julialang.org/u/cjdoris)\
**Post date:** [December 20, 2021, 8:29pm UTC](https://discourse.julialang.org/t/defining-a-custom-hash-function/73395/3 "2021-12-20T20:29:44Z")

</div>

You can overload `hash` for your own types however you like. You must also overload `==`. See the docstring for details.

Don’t overload `hash` for `Vector` though, that would be type piracy and would break stuff.

---

<div class="post-metadata">

**Author:** ![simeonschaub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/simeonschaub/32/216566_2.png) [@simeonschaub](https://discourse.julialang.org/u/simeonschaub)\
**Post date:** [December 20, 2021, 8:58pm UTC](https://discourse.julialang.org/t/defining-a-custom-hash-function/73395/4 "2021-12-20T20:58:38Z")

</div>

> [@jzr](#):
>
> Is that true? I would think they can be separate keys because equality should still be checked for lookup.

You need to ensure `hash` and `isequal` are compatible, e.g. `isequal(x, y)` always implies `hash(x) == hash(y)`. Otherwise the behavior for `Set`s and `Dict`s is not well defined. In your case, you are actually creating a hash collision, which is generally fine (although your particular `hash` function would of course completely defeat the purpose of a hash map in the first place), but since they are not `isequal`, two separate entries will still be created.

> [@cjdoris](#):
>
> You can overload `hash` for your own types however you like. You must also overload `==` .

More precisely, `hash` only needs to be compatible with `isequal`. `isequal` falls back to `==` by default, but `==` is afforded some more liberties like `NaN != NaN` for floating point variables.

---

<div class="post-metadata">

**Author:** ![jzr](https://avatars.discourse-cdn.com/v4/letter/j/eb9ed0/32.png) [@jzr](https://discourse.julialang.org/u/jzr)\
**Post date:** [December 20, 2021, 9:08pm UTC](https://discourse.julialang.org/t/defining-a-custom-hash-function/73395/5 "2021-12-20T21:08:59Z")

</div>

> [@simeonschaub](#):
>
> You need to ensure `hash` and `isequal` are compatible, e.g. `isequal(x, y)` always implies `hash(x) == hash(y)` . Otherwise the behavior for `Set` s and `Dict` s is not well defined.

The OP question is whether it is safe to _coarsen_ the hash. Coarsening is always safe but may reduce performance; refining is the risky operation.

---

<div class="post-metadata">

**Author:** ![simeonschaub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/simeonschaub/32/216566_2.png) [@simeonschaub](https://discourse.julialang.org/u/simeonschaub)\
**Post date:** [December 20, 2021, 9:10pm UTC](https://discourse.julialang.org/t/defining-a-custom-hash-function/73395/6 "2021-12-20T21:10:28Z")

</div>

Yes sorry, I made that a little more clear in my answer.

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://discourse.julialang.org/u/jules)\
**Post date:** [December 20, 2021, 9:12pm UTC](https://discourse.julialang.org/t/defining-a-custom-hash-function/73395/7 "2021-12-20T21:12:31Z")

</div>

Yes I want to avoid that two bezier paths with the same commands are stored as two different entries in a dict, so I thought I’d make the hash dependent on command vector content, not command vector identity. Otherwise the dict could fill up with essentially the same path every time a plot is made.

---

<div class="post-metadata">

**Author:** ![piever](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/piever/32/1815_2.png) [@piever](https://discourse.julialang.org/u/piever)\
**Post date:** [December 20, 2021, 9:40pm UTC](https://discourse.julialang.org/t/defining-a-custom-hash-function/73395/8 "2021-12-20T21:40:59Z")

</div>

IIUC, the issue is that the vector of command is stored in a `BezierPath` struct, but checking `isequal` on `BezierPath` by default will call `===` on the struct fields.

If you want a `BezierPath` type where both `isequal` and `hash` check for “content equality” rather than “vector identity”, you can use [https://github.com/andrewcooke/AutoHashEquals.jl](https://github.com/andrewcooke/AutoHashEquals.jl). It should be as simple as adding `@auto_hash_equals` in front of the struct definition.
