比较一个字段golang以外的结构

I am comparing two structs and want to ignore a single field while doing so.

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) \eturns true Desired outcome
  fmt.Println(reflect.DeepEqual(a,b)) \eturns false

}

reflect.DeepEqual() does not allow for us to ignore a field and have manually done the comparison one field at a time.

What is the idiomatic way to go about this?

1. With embedding

One option would be to group fields that should take part in the comparison into a different struct which you can embed in your original. When doing the comparison, just compare the embedded fields:

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
}

Try it on the Go Playground.

If the struct does not contain pointers, slices, maps etc., you can simply compare the embedded values using ==. Otherwise use reflect.DeepEqual() to compare them.

2. Temporarily modifying the excludable fields

You may also choose to temporarily modify the fields you don't want to compare: make them equal so the comparison result will only depend on the rest of the fields:

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

Try it on the Go Playground.

Another variation is to make a copy of one of the struct values, and modify and compare that to the other, so no need to restore the original, also this is "more concurrent-friendly":

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

Try this on the Go Playground.

3. Implement your own, custom comparison

If you can't or don't want to go with the above solutions, you can always create your own:

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


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

Try this on the Go Playground.

Notes:

This isn't "friendly" at first, as the custom compare() function requires you to check all involved fields, but its implementation may use the above methods, e.g. (try it on the 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)
}

You could also pass pointers to compare() to avoid copying the original struct, e.g. (try it on the 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)
}

Idiomatic way would be to implement your own func (o MyStruct) Equal(o2 MyStruct) bool method.

If your fields are exported, you may use reflect to check them. 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