# Create a set with custom hash and isequal

**URL:** <https://discourse.julialang.org/t/create-a-set-with-custom-hash-and-isequal/127862>\
**Category:** General Usage\
**Tags:** set, hash\
**Created:** [April 8, 2025, 8:08pm UTC](https://discourse.julialang.org/t/create-a-set-with-custom-hash-and-isequal/127862 "2025-04-08T20:08:07Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![jlbosse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jlbosse/32/11274_2.png) [@jlbosse](https://discourse.julialang.org/u/jlbosse)\
**Post date:** [April 8, 2025, 8:08pm UTC](https://discourse.julialang.org/t/create-a-set-with-custom-hash-and-isequal/127862/1 "2025-04-08T20:08:07Z")

</div>

Is it possible to create a `Set{MyType}` that uses a custom `hash` and `isequal` functions s.t. it is essentially a set of equivalence classes w.r.t the equivalence relation defined by my `hash` and `isequal`? I want to do this without loosing my standard `isequal` that checks for full equality, and not only equality w.r.t to my equivalence relation.

See below for the desired behaviour:

```julia
struct MyType
    coeff::Float64
    indices::Vector{Int}
end

Base.hash(x::MyType, h::UInt) = hash(x.indices, h)
Base.isequal(x::MyType, y::MyType) = (x.coeff == y.coeff) && (x.indices == y.indices)

a = MyType(1., [2, 3])
b = MyType(2., [2, 3])

a == b
# False, this is desired

s = Set([a, b])
# Set{MyType} with 2 elements:
# MyType(1.0, [2, 3])
# MyType(2.0, [2, 3])

# But I want this to be
# Set{MyType} with 1 element:
# MyType(1.0, [2, 3])
#
# or 
#
# Set{MyType} with 1 element:
# MyType(2.0, [2, 3])

```

---

<div class="post-metadata">

**Author:** ![matthias314](https://avatars.discourse-cdn.com/v4/letter/m/a88e4f/32.png) [@matthias314](https://discourse.julialang.org/u/matthias314)\
**Post date:** [April 9, 2025, 12:33pm UTC](https://discourse.julialang.org/t/create-a-set-with-custom-hash-and-isequal/127862/2 "2025-04-09T12:33:31Z")

</div>

I’m not aware of a set type that does this. However, I think this would be a useful feature. For example, I might have a fast hash function for some custom type `V{T} <: AbstractVector{T}`, but it is not compatible with the default `hash` for vectors. This incompatibility does not matter for a `Set` with element type `V{T}` (or a `Dict` with this key type). Hence the custom hash function would speed things up in such a scenario.

---

<div class="post-metadata">

**Author:** ![barucden](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/barucden/32/26154_2.png) [@barucden](https://discourse.julialang.org/u/barucden)\
**Post date:** [April 9, 2025, 12:53pm UTC](https://discourse.julialang.org/t/create-a-set-with-custom-hash-and-isequal/127862/3 "2025-04-09T12:53:23Z")

</div>

I can think of a solution:

```julia
struct MyType
    coeff::Float64
    indices::Vector{Int}
end

Base.hash(x::MyType, h::UInt) = hash(x.coeff, hash(x.indices, h))
Base.isequal(x::MyType, y::MyType) = (x.coeff == y.coeff) && (x.indices == y.indices)

a = MyType(1., [2, 3])
b = MyType(2., [2, 3])

# False
@info a == b 
# Set(MyType[MyType(1.0, [2, 3]), MyType(2.0, [2, 3])])
@info Set([a, b]) 

struct MyWrapper
    x::MyType
end

Base.hash(u::MyWrapper, h::UInt) = hash(u.x.indices, h)
Base.isequal(u::MyWrapper, v::MyWrapper) = isequal(u.x.indices, v.x.indices)

# Set(MyWrapper[MyWrapper(MyType(2.0, [2, 3]))])
@info Set([MyWrapper(a), MyWrapper(b)]) 

```

It’s verbose, I would say. But it’s decently clean: Two `MyType`s are equivalent if `coeff`s and `indices`s match; two `MyWrapper`s are equivalent if `indices`s of the underlying `MyType`s match.

---

<div class="post-metadata">

**Author:** ![jlbosse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jlbosse/32/11274_2.png) [@jlbosse](https://discourse.julialang.org/u/jlbosse)\
**Post date:** [April 9, 2025, 12:53pm UTC](https://discourse.julialang.org/t/create-a-set-with-custom-hash-and-isequal/127862/4 "2025-04-09T12:53:48Z")

</div>

In the end I settled to represent my `Set{MyType}` as a wrapped `Dict{Vector{Int}, Float64}`. Some (un)wrapping was required to turn insertion of a `MyType` into insertion of a `Vector{Int} => Float64` into that dict and make iteration yield `MyType` instead of `Pair{Vector{Int}, Float64}`, but I hope that the compiler is able to pretty much compile all these (un)wrapping operations away.

---

<div class="post-metadata">

**Author:** ![jlbosse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jlbosse/32/11274_2.png) [@jlbosse](https://discourse.julialang.org/u/jlbosse)\
**Post date:** [April 9, 2025, 12:56pm UTC](https://discourse.julialang.org/t/create-a-set-with-custom-hash-and-isequal/127862/5 "2025-04-09T12:56:50Z")

</div>

Great minds think alike 😃

Your solutoin works as well and is similar to my solution above in that additional (un)wrapping would be needed to be able to directly insert `MyType`s and have iteration yield `MyType`s instead of `MyWrapper`.

---

<div class="post-metadata">

**Author:** ![barucden](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/barucden/32/26154_2.png) [@barucden](https://discourse.julialang.org/u/barucden)\
**Post date:** [April 9, 2025, 1:28pm UTC](https://discourse.julialang.org/t/create-a-set-with-custom-hash-and-isequal/127862/6 "2025-04-09T13:28:44Z")

</div>

> [@jlbosse](#):
>
> Great minds think alike 😃

😀

Using `Dict` is way more convenient. You can avoid wrapping/unwrapping too:

```julia
function store_mytype!(dict::AbstractDict, x::MyType)
    get!(dict, x.indices) do
        x
    end
end

dict = Dict{Vector{Int}, MyType}()
store_mytype!(dict, a)
store_mytype!(dict, b)

# Or use: store_mytype!.(Ref(dict), [a, b])

# MyType[MyType(1.0, [2, 3])]
@info values(dict)

```

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [April 9, 2025, 3:07pm UTC](https://discourse.julialang.org/t/create-a-set-with-custom-hash-and-isequal/127862/7 "2025-04-09T15:07:04Z")

</div>

> [@jlbosse](#):
>
> Is it possible to create a `Set{MyType}` that uses a custom `hash` and `isequal` functions

See:

> [@Dictionary with custom hash function](https://discourse.julialang.org/t/dictionary-with-custom-hash-function/49168/4):
>
> Just wrap your keys in a new type: struct MyKey val::UInt end Base.hash(a::MyKey, h::UInt) = xor(a.val, h) Base.(==)(a::MyKey, b::MyKey) = a.val == b.val

---

<div class="post-metadata">

**Author:** ![matthias314](https://avatars.discourse-cdn.com/v4/letter/m/a88e4f/32.png) [@matthias314](https://discourse.julialang.org/u/matthias314)\
**Post date:** [April 9, 2025, 7:31pm UTC](https://discourse.julialang.org/t/create-a-set-with-custom-hash-and-isequal/127862/8 "2025-04-09T19:31:09Z")

</div>

> [@Dictionary with custom hash function](https://discourse.julialang.org/t/dictionary-with-custom-hash-function/49168/4):
>
> Just wrap your keys in a new type

Sure, but then you have to unwrap the elements again when you want to use methods defined for the original type – if that it possible at all. Maybe the elements are used inside a function defined in some third-party package. It would be nice if everything could be done under the hood.
