Home > Software design >  Go cmp - how to define custom equality for a map with a struct as its keys?
Go cmp - how to define custom equality for a map with a struct as its keys?

Time:11-09

Given a map that uses a struct as its key, where the values of the struct are pointers to another struct:

type Dog struct {
    Name string
}

type Cat struct {
    Name string
}

type MapKey struct {
    dog *Dog
    cat *Cat
}

myMap := make(map[MapKey]int)

How would I use the cmp package to make the below maps equal, where they are considered equal because the MapKey has the same values (reflect.DeepEquals or cmp.Equals)?

keyOne := MapKey{
    &Dog{Name: "bob"},
    &Cat{Name: "clive"},
}
keyTwo := MapKey{
    &Dog{Name: "bob"},
    &Cat{Name: "clive"},
}

got := map[MapKey]int{
    keyOne: 1,
}
want := map[MapKey]int{
    keyTwo: 1,
}

In the cmp documentation, it says I could use cmpopts.SortMaps (https://pkg.go.dev/github.com/google/go-cmp/cmp#Equal), however I don't see how this is relevant to my scenario.

I've tried defining a custom Equals function on the MapKey struct but it never gets called.

Go playground to reproduce this: https://go.dev/play/p/qMxaya3S26M

CodePudding user response:

The cmp.Equal is called with parameters of type map[MapKey]int, not MapKey

So the custom Equal function has to be defined on the type map[MapKey]int.

But to define this function, we need to define a new type from map[MapKey]int.

Here's a playground to the full working example: https://go.dev/play/p/deteHANWQ_3

type MapKeyInt map[MapKey]int

func (m MapKeyInt) Equal(other MapKeyInt) bool {
    if len(m) != len(other) {
        return false
    }

    keys, keysOther := make([]MapKey, 0), make([]MapKey, 0)
    values, valuesOther := make([]int, 0), make([]int, 0)

    for k, v := range m {
        keys = append(keys, k)
        values = append(values, v)
    }
    for k, v := range other {
        keysOther = append(keysOther, k)
        valuesOther = append(valuesOther, v)
    }

    for i := 0; i < len(m); i   {
        if (keys[i].dog.Name != keysOther[i].dog.Name) || (keys[i].cat.Name != keysOther[i].cat.Name) {
            return false
        }
        if values[i] != valuesOther[i] {
            return false
        }
    }

    return true
}
  • Related