golang-set
golang-set copied to clipboard
Suggestion: Add comparator for structs
What is considered comparable in Go?
Structsif all of their fields are also comparable independently
Would be great to be able to pass a key (or function) to for struct comparison instead of relying on complete field equality.
Particularly useful when comparing structs with incomplete data (e.g. from an API) with complete data (e.g. from DB).
Hello,
Thanks for the recommendation! I'm certainly not opposed to changes that would be considered general enough that others would find useful and if you have an API in mind I'd love to hear more details.
This package is so widely used it would be help to get more context and allow others to chime in.
Here's some pseudo code for the pattern I've got. Please forgive errors.
type Entity struct {
ID uint64
Data1 string
Data2 bool
// ...
UpdatedAt *time.Time
}
func Update(apiEntities []*Entity) error {
dbEntities = db.ListEntities()
origSet := mapset.NewSet(dbEntities...)
updateSet := mapset.NewSet(apiEntities...)
added := updateSet.Difference(origSet)
// Process newly added entries
removed := origSet.Difference(updateSet)
// Process deleted entries
}
Entity's that come in via the API don't have an ID populated, this causes all data to not match.
Also in this case UpdatedAt should be ignored when looking for equality.
If we could pass a function to do the comparison, that would be a nice general solution. Something like:
func entityEqual(a Entity, b Entity) bool {
// Whatever logic here
return a.Data1 == b.Data1 && a.Data2 == b.Data2
}
func Update(apiEntities []*Entity) error {
// ...
added := updateSet.Difference(origSet, entityEqual)
// ...
}
Sounds good I’ll be looking at this next week due to a new project deadline so please forgive the upcoming delay!
On Wed, Jul 10, 2024 at 4:01 PM Michael Standen @.***> wrote:
Here's some pseudo code for the pattern I've got. Please forgive errors.
type Entity struct { ID uint64 Data1 string Data2 bool // ... UpdatedAt *time.Time } func Update(apiEntities []*Entity) error { dbEntities = db.ListEntities() origSet := mapset.NewSet(dbEntities...) updateSet := mapset.NewSet(apiEntities...)
added := updateSet.Difference(origSet) // Process newly added entries
removed := origSet.Difference(updateSet) // Process deleted entries }
Entity's that come in via the API don't have an ID populated, this causes all data to not match. Also in this case UpdatedAt should be ignored when looking for equality.
If we could pass a function to do the comparison, that would be a nice general solution. Something like:
func entityEqual(a Entity, b Entity) bool { // Whatever logic here return a.Data1 == b.Data1 && a.Data2 == b.Data2 } func Update(apiEntities []*Entity) error { // ... added := updateSet.Difference(origSet, entityEqual) // ... }
— Reply to this email directly, view it on GitHub https://github.com/deckarep/golang-set/issues/139#issuecomment-2221666023, or unsubscribe https://github.com/notifications/unsubscribe-auth/AABQ73R6UMVPORVJYVXZG23ZLW4NTAVCNFSM6AAAAABKVWYCHKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDEMRRGY3DMMBSGM . You are receiving this because you commented.Message ID: @.***>
@ScreamingHawk - circling back on this seems like this would be a fairly large breaking change. I understand what your concern is but I do feel like this would possibly complicate the API. I'm still thinking about it.
The safest IMO would be to implement a new interface entirely with this functionality. I for one would prefer the existing behavior unchanged by default, but this functionality could also be useful if implemented in a generic way. Something like NewSetWithComparator that accepts a user-defined function for determining equality along with the usual list of initial items?
This would definitely be a larger effort, and might be best suited to a sub-package instead of the main one to keep the changes contained in a non-breaking way
The main challenge operationally would be that deviating from Golang's native equality check renders direct map key references no longer useful. The set would either need to iterate through elements and check them (which doesn't scale) or implement a form of hash keying (which greatly adds to complexity), neither of which is ideal when compared to the existing implementation