比较除了一个字段golang之外的结构

时间:2017-11-06 10:03:42

标签: go struct

我正在比较两个结构,并希望忽略单个字段。

type test struct {
  name string
  time string
} 

func main() {
  a := test{"testName", time.Now().Format(time.UnixTime)}
  // after some time
  b := test{"testName", time.Now().Format(time.UnixTime)}

  fmt.Println(a.Name == b.Name) \\ returns true Desired outcome
  fmt.Println(reflect.DeepEqual(a,b)) \\ returns false

}

reflect.DeepEqual()不允许我们忽略某个字段,并且一次手动完成一个字段的比较。

这是什么惯用法?

3 个答案:

答案 0 :(得分:10)

惯用法是实现您自己的func (o MyStruct) Equal(o2 MyStruct) bool方法。

答案 1 :(得分:4)

1。嵌入

一种选择是将参与比较的字段分组到可以嵌入原文中的不同结构中。在进行比较时,只需比较嵌入字段:

type Person struct {
    Name string
    Age  int
}

type Doc struct {
    Person
    Created time.Time
}

func main() {
    d1 := Doc{
        Person:  Person{"Bob", 21},
        Created: time.Now(),
    }
    time.Sleep(time.Millisecond)
    d2 := Doc{
        Person:  Person{"Bob", 21},
        Created: time.Now(),
    }

    fmt.Println(d1 == d2)               // false
    fmt.Println(d1.Person == d2.Person) // true
}

Go Playground上尝试。

如果结构不包含指针,切片,贴图等,则可以使用==简单地比较嵌入值。否则,请使用reflect.DeepEqual()进行比较。

2。暂时修改排他性字段

您也可以选择暂时修改您不想比较的字段:使它们相等,这样比较结果将仅取决于其他字段:

a := test{"testName", time.Now().Format(time.StampMilli)}
time.Sleep(time.Millisecond)
b := test{"testName", time.Now().Format(time.StampMilli)}

// Save and make excluded fields equal:
old := a.time
a.time = b.time

fmt.Println(a.name == b.name)        // true
fmt.Println(reflect.DeepEqual(a, b)) // true

// Restore:
a.time = old

Go Playground上尝试。

另一个变体是制作一个struct值的副本,并将其修改并与另一个进行比较,因此无需恢复原始值,这也是“更兼容并发”:

// Make copies and make excluded fields equal:
a2 := a
a2.time = b.time

fmt.Println(a2.name == b.name)        // true
fmt.Println(reflect.DeepEqual(a2, b)) // true

Go Playground上尝试此操作。

3。实施您自己的自定义比较

如果您不能或不想使用上述解决方案,您可以随时创建自己的解决方案:

func compare(a, b test) bool {
    return a.name == b.name
}


fmt.Println(a.name == b.name) // true
fmt.Println(compare(a, b))    // true

Go Playground上尝试此操作。

备注:

这首先不是“友好”,因为自定义compare()功能要求您检查所有涉及的字段,但其实现可能会使用上述方法,例如: (在Go Playground上试试):

func compare(a, b test) bool {
    a.time = b.time // We're modifying a copy, so no need to make another copy
    return reflect.DeepEqual(a, b)
}

您还可以将指针传递给compare()以避免复制原始结构,例如(在Go Playground上试试):

fmt.Println(a.name == b.name) // true
fmt.Println(compare(&a, &b))  // true

func compare(a, b *test) bool {
    a2 := new(test)
    *a2 = *a
    a2.time = b.time
    return reflect.DeepEqual(a2, b)
}

答案 2 :(得分:0)

如果您的字段已导出,则可以使用reflect进行检查。 E.g:

package main

import (
    "fmt"
    "reflect"
)

type Foo struct {
    Name string
    Date int
}

func (f *Foo) EqualExcept(other *Foo, ExceptField string) bool {
    val := reflect.ValueOf(f).Elem()
    otherFields := reflect.Indirect(reflect.ValueOf(other))

    for i := 0; i < val.NumField(); i++ {
        typeField := val.Type().Field(i)
        if typeField.Name == ExceptField {
            continue
        }

        value := val.Field(i)
        otherValue := otherFields.FieldByName(typeField.Name)

        if value.Interface() != otherValue.Interface() {
            return false
        }
    }
    return true
}

func main() {
    f := &Foo{
        "Drew",
        30,
    }

    f2 := &Foo{
        "Drew",
        50,
    }

    fmt.Println(f.EqualExcept(f2, "Date"))
    fmt.Println(f.EqualExcept(f2, "Name"))

}

https://play.golang.org/p/DlhE8aZGwa