Go cmp – 如何为以结构体为键的映射定义自定义相等性?

huangapple go评论82阅读模式
英文:

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

问题

给定一个以结构体作为键的地图,其中结构体的值是指向另一个结构体的指针:

type Dog struct {
	Name string
}

type Cat struct {
	Name string
}

type MapKey struct {
	dog *Dog
	cat *Cat
}

myMap := make(map[MapKey]int)

我该如何使用cmp包使下面的地图相等,其中它们被认为是相等的,因为MapKey具有相同的值(使用reflect.DeepEquals或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,
}

在cmp文档中,它说我可以使用cmpopts.SortMaps(https://pkg.go.dev/github.com/google/go-cmp/cmp#Equal),但我不明白这与我的情况有何关系。

我尝试在MapKey结构上定义一个自定义的Equals函数,但它从未被调用。

可以使用Go Playground重现此问题:
https://go.dev/play/p/qMxaya3S26M

英文:

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

答案1

得分: 1

cmp.Equal是使用map[MapKey]int类型的参数调用的,而不是MapKey类型。

因此,自定义的Equal函数必须在map[MapKey]int类型上进行定义。

但是,为了定义这个函数,我们需要从map[MapKey]int定义一个新类型。

这是一个完整工作示例的Playground链接: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
}
英文:

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 &lt; 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
}

huangapple
  • 本文由 发表于 2022年11月7日 21:29:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/74347375.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定