我想检查两个结构、切片和映射是否相等。
但是我遇到了以下代码的问题。请参阅我在相关行的评论。
package main
import (
"fmt"
"reflect"
)
type T struct {
X int
Y string
Z []int
M map[string]int
}
func main() {
t1 := T{
X: 1,
Y: "lei",
Z: []int{1, 2, 3},
M: map[string]int{
"a": 1,
"b": 2,
},
}
t2 := T{
X: 1,
Y: "lei",
Z: []int{1, 2, 3},
M: map[string]int{
"a": 1,
"b": 2,
},
}
fmt.Println(t2 == t1)
//error - invalid operation: t2 == t1 (struct containing []int cannot be compared)
fmt.Println(reflect.ValueOf(t2) == reflect.ValueOf(t1))
//false
fmt.Println(reflect.TypeOf(t2) == reflect.TypeOf(t1))
//true
//Update: slice or map
a1 := []int{1, 2, 3, 4}
a2 := []int{1, 2, 3, 4}
fmt.Println(a1 == a2)
//invalid operation: a1 == a2 (slice can only be compared to nil)
m1 := map[string]int{
"a": 1,
"b": 2,
}
m2 := map[string]int{
"a": 1,
"b": 2,
}
fmt.Println(m1 == m2)
// m1 == m2 (map can only be compared to nil)
}
http://play.golang.org/p/AZIzW2WunI
您可以使用 reflect.DeepEqual,也可以实现自己的函数(在性能方面比使用反射更好):
http://play.golang.org/p/CPdfsYGNy_
m1 := map[string]int{
"a":1,
"b":2,
}
m2 := map[string]int{
"a":1,
"b":2,
}
fmt.Println(reflect.DeepEqual(m1, m2))
reflect.DeepEqual
通常被错误地用于比较两个类似的结构,就像您的问题一样。
cmp.Equal
是比较结构的更好工具。
要了解为什么不建议使用反射,让我们看一下 documentation:
如果结构值的对应字段(导出和未导出)深度相等,则结构值深度相等。 .... 数字、布尔值、字符串和通道 - 如果它们使用 Go 的 == 运算符相等,则它们是完全相等的。
如果我们比较同一 UTC 时间的两个 time.Time
值,如果它们的元数据时区不同,则 t1 == t2
将为 false。
go-cmp
查找 Equal()
方法并使用它来正确比较时间。
例子:
m1 := map[string]int{
"a": 1,
"b": 2,
}
m2 := map[string]int{
"a": 1,
"b": 2,
}
fmt.Println(cmp.Equal(m1, m2)) // will result in true
重要的提示:
使用 cmp.Equal
时要小心,因为它是 may lead to a panic condition
它仅用于测试,因为性能不是目标,如果无法比较值,它可能会恐慌。它的恐慌倾向意味着它不适合虚假恐慌可能致命的生产环境。
go-cmp
而不是 reflect
非常重要。
以下是您如何推出自己的功能 http://play.golang.org/p/Qgw7XuLNhb
func compare(a, b *T) bool {
if a == b {
return true
}
if a.X != b.X || a.Y != b.Y {
return false
}
if len(a.Z) != len(b.Z) || len(a.M) != len(b.M) {
return false
}
for i, v := range a.Z {
if b.Z[i] != v {
return false
}
}
for k, v := range a.M {
if b.M[k] != v {
return false
}
}
return true
}
更新:去 1.18
import (
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
)
func compare(a, b *T) bool {
if a == b {
return true
}
if a.X != b.X {
return false
}
if a.Y != b.Y {
return false
}
if !slices.Equal(a.Z, b.Z) {
return false
}
return maps.Equal(a.M, b.M)
}
if len(a.Z) != len(b.Z) || len(a.M) != len(b.M) { return false }
,因为其中之一可能有额外的字段。
if a == b ...
(因此来自 OP 的问题)。我想你想跟进肖恩的笔记。您必须将 T
更改为 *T
,然后第一个 if()
才能编译(并按预期工作)。
如果您打算在测试中使用它,因为 July 2017 您可以将 cmp.Equal
与 cmpopts.IgnoreFields
选项一起使用。
func TestPerson(t *testing.T) {
type person struct {
ID int
Name string
}
p1 := person{ID: 1, Name: "john doe"}
p2 := person{ID: 2, Name: "john doe"}
println(cmp.Equal(p1, p2))
println(cmp.Equal(p1, p2, cmpopts.IgnoreFields(person{}, "ID")))
// Prints:
// false
// true
}
如果要比较简单的一级结构,最好且最简单的方法是 if
语句。
喜欢这个if s1 == s2
这是一个简单的例子:
type User struct {
name string
email string
}
func main() {
u1 := User{
name: "Iron Man",
email: "ironman@avengers.com",
}
u2 := User{
name: "Iron Man",
email: "ironman@avengers.com",
}
// Comparing 2 structs
if u1 == u2 {
fmt.Println("u1 is equal to u2")
} else {
fmt.Println("u1 is not equal to u2")
}
}
结果:u1 is equal to u2
您可以使用此 here。
比较地图的新方法
该提案 (https://github.com/golang/go/issues/47649) 是 Go 泛型未来实现的一部分,它引入了一个新函数来比较两个地图 maps.Equal
:
// Equal reports whether two maps contain the same key/value pairs.
// Values are compared using ==.
func Equal[M1, M2 constraints.Map[K, V], K, V comparable](m1 M1, m2 M2) bool
示例使用
strMapX := map[string]int{
"one": 1,
"two": 2,
}
strMapY := map[string]int{
"one": 1,
"two": 2,
}
equal := maps.Equal(strMapX, strMapY)
// equal is true
maps
包位于 golang.org/x/exp/maps
。这是实验性的,不属于 Go 兼容性保证。他们的目标是将其移入 Go 1.19 中的 std lib
您可以在 gotip 操场上看到它https://gotipplay.golang.org/p/M0T6bCm1_3m
cmp.Equal(p1, p2, cmpopts.IgnoreFields(person{}, "ID"))
?