如何比较两个结构体、切片或映射是否相等?

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

How to compare if two structs, slices or maps are equal?

问题

我想检查两个结构体、切片和映射是否相等。

但是我在以下代码中遇到了问题。请看我在相关行的注释。

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)
	//错误 - 无效操作: t2 == t1(包含[]int的结构体无法比较)

	fmt.Println(reflect.ValueOf(t2) == reflect.ValueOf(t1))
	//false
	fmt.Println(reflect.TypeOf(t2) == reflect.TypeOf(t1))
	//true

	//更新:切片或映射
	a1 := []int{1, 2, 3, 4}
	a2 := []int{1, 2, 3, 4}

	fmt.Println(a1 == a2)
	//无效操作: a1 == a2(切片只能与nil比较)

	m1 := map[string]int{
		"a": 1,
		"b": 2,
	}
	m2 := map[string]int{
		"a": 1,
		"b": 2,
	}
	fmt.Println(m1 == m2)
	//无效操作: m1 == m2(映射只能与nil比较)
}

你可以在这里查看代码:http://play.golang.org/p/AZIzW2WunI

英文:

I want to check if two structs, slices and maps are equal.

But I'm running into problems with the following code. See my comments at the relevant lines.

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

答案1

得分: 214

你可以使用reflect.DeepEqual,或者你可以实现自己的函数(性能上比使用反射更好):

m1 := map[string]int{
    "a": 1,
    "b": 2,
}
m2 := map[string]int{
    "a": 1,
    "b": 2,
}
fmt.Println(reflect.DeepEqual(m1, m2))

请注意,reflect.DeepEqual函数用于比较两个值是否深度相等。

英文:

You can use reflect.DeepEqual, or you can implement your own function (which performance wise would be better than using reflection):

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))

答案2

得分: 129

reflect.DeepEqual经常被错误地用于比较两个类似的结构体,就像你的问题中一样。

cmp.Equal是一个更好的用于比较结构体的工具。

为了了解为什么不建议使用反射,让我们看一下文档

如果结构体的对应字段(包括导出和非导出字段)是深度相等的,那么结构体值就是深度相等的。

....

数字、布尔值、字符串和通道——如果它们使用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)) // 将返回true

重要提示:

在使用cmp.Equal时要小心,因为它可能会导致恐慌条件

它只应该在测试中使用,因为性能不是目标,如果无法比较值,它可能会引发恐慌。它容易引发恐慌,这意味着在可能导致严重问题的生产环境中不适用。

英文:

reflect.DeepEqual is often incorrectly used to compare two like structs, as in your question.

cmp.Equal is a better tool for comparing structs.

To see why reflection is ill-advised, let's look at the documentation:

> Struct values are deeply equal if their corresponding fields, both exported and unexported, are deeply equal.
>
> ....
>
> numbers, bools, strings, and channels - are deeply equal if they are equal using Go's == operator.

If we compare two time.Time values of the same UTC time, t1 == t2 will be false if their metadata timezone is different.

go-cmp looks for the Equal() method and uses that to correctly compare times.

Example:

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

Important Note:

Be careful when using cmp.Equal as it may lead to a panic condition

> It is intended to only be used in tests, as performance is not a goal
> and it may panic if it cannot compare the values. Its propensity
> towards panicking means that its unsuitable for production
> environments where a spurious panic may be fatal.

答案3

得分: 28

这是如何编写自定义函数的示例代码: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
}

更新:Go 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)
}

希望这对你有帮助!

英文:

Here's how you'd roll your own function 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
}

Update: Go 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)
}

答案4

得分: 18

如果你打算在测试中使用它,自从2017年7月起,你可以使用cmp.Equalcmpopts.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")))

    // 输出:
    // false
    // true
}
英文:

If you intend to use it in tests, since July 2017 you can use cmp.Equal with cmpopts.IgnoreFields option.

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
}

答案5

得分: 11

如果你正在进行单元测试时进行比较,一个方便的替代方法是使用testify中的EqualValues函数。

英文:

If you're comparing them in unit test, a handy alternative is EqualValues function in testify.

答案6

得分: 3

如果你想比较简单的一级结构体,最好和简单的方法是使用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",
}
// 比较两个结构体
if u1 == u2 {
fmt.Println("u1 等于 u2")
} else {
fmt.Println("u1 不等于 u2")
}
}

结果:u1 等于 u2

你可以在这里进行测试。

英文:

If you want to compare simple one-level structs, the best and simple method is the if statement.

Like this if s1 == s2

Here is a simple example:

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")
}
}

Result: u1 is equal to u2

You can play with this here.

答案7

得分: 3

比较地图的新方法

这个提案(https://github.com/golang/go/issues/47649)是Go泛型未来实现的一部分,引入了一个新的函数来比较两个地图,maps.Equal

// Equal报告两个地图是否包含相同的键值对。
// 使用==比较值。
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为true

maps包可以在golang.org/x/exp/maps中找到。这是实验性的,不在Go的兼容性保证之内。他们计划在Go 1.19中将其移入标准库。

你可以在gotip playground中看到它的工作示例
https://gotipplay.golang.org/p/M0T6bCm1_3m

英文:

New way to compare maps

This proposal (https://github.com/golang/go/issues/47649) that is part of the future implementation of Go generics introduces a new function to compare two maps, 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

Example use

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 package is found in golang.org/x/exp/maps. This is experimental and outside of Go compatibility guarantee. They aim to move it into the std lib in Go 1.19

You can see it working in gotip playground
https://gotipplay.golang.org/p/M0T6bCm1_3m

huangapple
  • 本文由 发表于 2014年7月2日 22:41:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/24534072.html
匿名

发表评论

匿名网友

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

确定