卓越飞翔博客卓越飞翔博客

卓越飞翔 - 您值得收藏的技术分享站
技术文章64334本站已运行4115

无论 Go 中元素的顺序如何,如何在语义上比较结构与嵌套切片

无论 go 中元素的顺序如何,如何在语义上比较结构与嵌套切片

无论Go中元素的顺序如何,如何在语义上比较结构与嵌套切片是一个常见问题。在Go语言中,结构体是一种聚合数据类型,而切片则是一种动态数组。php小编子墨将为您解答这个问题。在比较结构体时,只有当所有成员都是可比较的类型时,它们才是可比较的。而在比较嵌套切片时,我们需要逐级比较切片中的元素。如果切片的元素类型是不可比较的,我们需要使用递归方法来比较切片的每个元素。无论是结构体还是嵌套切片,我们都可以通过遍历元素来比较它们。

问题内容

给出下一个类型结构定义:

type A struct {
    Id  int
    Bs  []B
    Sub C
}

type B struct {
    Id  int
    Str string
}

type C struct {
    Id int
    Ds []D
}

type D struct {
    Id  int
    Num int
}

我想测试 A 的接下来两个实例在语义上是否相等,无论所有层次结构级别中切片元素的顺序如何。

var want = &A{
    Id: 1,
    Bs: []B{{Id: 10, Str: "b10"}, {Id: 20, Str: "b20"}},
    Sub: C{
        Id: 100,
        Ds: []D{{Id: 101, Num: 1001}, {Id: 102, Num: 1002}},
    },
}

var got = &A{
    Id: 1,
    Bs: []B{{Id: 20, Str: "b20"}, {Id: 10, Str: "b10"}},
    Sub: C{
        Id: 100,
        Ds: []D{{Id: 102, Num: 1002}, {Id: 101, Num: 1001}},
    },
}

断言比较应返回true

解决方法

包 cmp 旨在成为 reflect.DeepEqual 的更强大、更安全的替代品用于比较两个值在语义上是否相等。

这里是语义相等结构比较的完整实现,无论切片元素在所有层次结构级别的顺序如何。

文件source.go

package main

type A struct {
    Id  int
    Bs  []B
    Sub C
}

type B struct {
    Id  int
    Str string
}

type C struct {
    Id int
    Ds []D
}

type D struct {
    Id  int
    Num int
}

func NewA() *A {
    return &A{
        Id: 1,
        Bs: []B{{Id: 20, Str: "b20"}, {Id: 10, Str: "b10"}},
        Sub: C{
            Id: 100,
            Ds: []D{{Id: 102, Num: 1002}, {Id: 101, Num: 1001}},
        },
    }
}

文件source_test.go

package main

import (
    "fmt"
    "testing"

    "github.com/google/go-cmp/cmp"
    "github.com/google/go-cmp/cmp/cmpopts"
)

var want = &A{
    Id: 1,
    Bs: []B{{Id: 10, Str: "b10"}, {Id: 20, Str: "b20"}},
    Sub: C{
        Id: 100,
        Ds: []D{{Id: 101, Num: 1001}, {Id: 102, Num: 1002}},
    },
}

func TestNewA(t *testing.T) {
    got := NewA()
    less := func(x, y any) bool {
        switch xv := x.(type) {
        case B:
            yv := y.(B)
            return fmt.Sprintf("%d-%s", xv.Id, xv.Str) < fmt.Sprintf("%d-%s", yv.Id, yv.Str)
        case D:
            yv := y.(D)
            return fmt.Sprintf("%d-%d", xv.Id, xv.Num) < fmt.Sprintf("%d-%d", yv.Id, yv.Num)
        default:
            return false
        }
    }
    if diff := cmp.Diff(want, got, cmpopts.SortSlices(less)); diff != "" {
        t.Errorf("mismatch:n%s", diff)
    }
}
卓越飞翔博客
上一篇: 如何在 VSCode 中为特定 .go 文件创建调试配置?
下一篇: Go:如何在地图和切片中使用接口
留言与评论(共有 0 条评论)
   
验证码:
隐藏边栏