如何比较struct,slice,map是否相等?

lei*_*lin 99 go go-reflect

我想检查两个结构是否相等,但有一些问题:

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)
}
Run Code Online (Sandbox Code Playgroud)

http://play.golang.org/p/AZIzW2WunI

One*_*One 134

你可以使用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))
Run Code Online (Sandbox Code Playgroud)

  • 是否可以忽略某些字段,例如 id,例如 `cmp.Equal(p1, p2, cmpopts.IgnoreFields(person{}, "ID"))`? (3认同)
  • 不要忽略字段,而是将 ID 与要比较的数据分开(通过嵌入结构);您可以与忽略 ID 进行比较。示例:https://go.dev/play/p/HvWunmktpkm (2认同)

Col*_*tel 49

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

cmp.Equal 是比较结构的更好工具.

要了解为什么反思是不明智的,让我们看看文档:

如果导出和未导出的相应字段非常相等,则结构值非常相等.

....

数字,bools,字符串和通道 - 如果使用Go的==运算符相等则非常相等.

如果我们比较time.Time相同UTC时间的两个值,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
Run Code Online (Sandbox Code Playgroud)

  • 对,就是这样!在编写测试时,使用`go-cmp`而不是`reflect`非常重要. (6认同)
  • @GeneralLeeSpeaking是错误的。从[cmp文档](https://godoc.org/github.com/google/go-cmp/cmp):“如果指针指向的基础值也相等,则指针是相等的” (2认同)
  • 根据 [cmp 文档](https://godoc.org/github.com/google/go-cmp/cmp),仅在编写测试时建议使用 cmp,因为如果对象不可比较,它可能会出现恐慌。 (2认同)

wst*_*wst 15

20177 月起,您可以使用cmp.Equalwithcmpopts.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
}
Run Code Online (Sandbox Code Playgroud)


Ili*_*oly 14

这是你如何推出自己的功能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
}
Run Code Online (Sandbox Code Playgroud)

  • 我建议添加`if len(aZ)!= len(bZ)|| len(aM)!= len(bM){return false}`,因为其中一个可能有额外的字段. (3认同)
  • @ Rick-777根本没有为切片定义比较.这就是语言设计者所希望的.它的定义并不像简单整数的比较那么简单.如果切片包含相同顺序的相同元素,则切片是否相等?但是,如果他们的能力不同呢?等等. (3认同)
  • if &a == &b { return true } 如果要比较的参数是按值传递的,这将永远不会评估为真。 (2认同)

辽北狠*_*北狠人 8

如果你比较他们在单元测试中,一个方便的选择是EqualValues功能作证


bla*_*een 5

去1.21

与下面相同,但maps.Equal现在位于标准库中,您可以直接使用它,而不必导入实验包或滚动自己的包。

去1.18~1.20

该提案(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
Run Code Online (Sandbox Code Playgroud)

使用示例

strMapX := map[string]int{
    "one": 1,
    "two": 2,
}
strMapY := map[string]int{
    "one": 1,
    "two": 2,
}

equal := maps.Equal(strMapX, strMapY)
// equal is true
Run Code Online (Sandbox Code Playgroud)

maps包位于golang.org/x/exp/maps. 这是实验性的,超出了 Go 兼容性保证。他们的目标是将其移至 Go 1.19 中的标准库中

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