Make Base.unique work with user-defined types

I am trying to make Base.unique work with an array of a custom type. A really trivial example might look like this

import Base.isequal
struct TestStruct
	x::String
end

Base.isequal(t1::TestStruct, t2::TestStruct) = length(t1.x) == length(t2.x)

#Should return just one element, instead returns exactly the input
unique([TestStruct("blah"), TestStruct("bluh")]) 

#Should return true, instead returns false
TestStruct("blah") == TestStruct("bluh")

I know unique(f, itr) exists, but it won’t work well for my actual problem. I want to be able to extend Base.isequal and have things like unique and == call it. How is this done?

isequal is the one that falls back on ==, so you want to extend ==.

The default implementation of isequal calls ==, so a type that does not involve floating-point values generally only needs to
define ==.

from isequal’s docstring. Also from there is:

isequal(x,y) must imply that hash(x) == hash(y)

and unique depends on this behaviour too. So defining Base.== and Base.hash should get you the behaviour you want:


julia> import Base: ==, hash

julia> ==(t1::TestStruct, t2::TestStruct) = length(t1.x) == length(t2.x)
== (generic function with 170 methods)

julia> TestStruct("blah") == TestStruct("bluh")
true

julia> hash(t::TestStruct) = unsigned(length(t.x))
hash (generic function with 69 methods)

julia> unique([TestStruct("blah"), TestStruct("bluh")])
1-element Vector{TestStruct}:
 TestStruct("blah")
2 Likes