Go-合并两个结构的值,取非空字段

时间:2018-06-15 01:55:01

标签: go struct

给定两个结构,其中任何一个字段的某些字段可能为null,那么合并它们的最优雅方式是什么? (如果两个都有一个非空的字段,那么取一边?)

我不确定如何在不比较每个字段的情况下执行此操作。

2 个答案:

答案 0 :(得分:1)

您还需要问自己以下问题:

  • 如果两者都是零怎么办?
  • 如果两者都是非零的话怎么办?
  • 并非所有类型都是无法使用的
  • 你如何处理嵌套?

在大多数情况下,第一个问题确实不是问题因为通常结果会是nil。在大多数情况下,第二个问题确实不是问题,因为通常你会定义一些优先级(即第一个参数胜过第二个参数)。

第三个有点棘手。您可以定义采取非零的任何一个。

嵌套也有点棘手。您可能希望"深度合并"一些结构或只是表面上合并一些结构。

无论如何,你可以用reflect做到这一点,但它会变得棘手(除非有人已经做过)。通过大约5分钟的黑客攻击你可以得到这个:

func f(a,b interface{}) {
    ra := reflect.ValueOf(a).Elem()
    rb := reflect.ValueOf(b).Elem()

    numFields := ra.NumField()

    for i := 0; i < numFields; i++ {
        field_a := ra.Field(i)
        field_b := rb.Field(i)

        switch field_a.Kind() {
        case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
            if field_a.IsNil() {
                field_a.Set(field_b)
            }
        }
    }
}

但这是完整的FAR,并且它只适用于导出的字段,因为访问未导出的字段对于reflect来说并不方便,并且需要更多的怪癖来完成它。它也没有递归,但如果你检测到嵌套结构等,你当然可以递归。

但是,如果你只为你的结构编写一个手动合并函数,它可能会更好。

答案 1 :(得分:1)

如果你不太在乎成本。我的解决方案效率很低。使用带有omitempty标记的json marshal和unmarshal。希望它可以帮助你。

package main

import (
    "fmt"
    "encoding/json"
)

type myStruct struct{
    Id int      `json:"id,omitempty"`
    Name string `json:"name,omitempty"`
    Sex bool `json:"sex,omitempty"`
    Age int `json:"age,omitempty"`
    Addr string `json:"addr,omitempty"`
}

func MergeStruct(to interface{},from interface{}) error{
    byte1,err := json.Marshal(to)
    if err != nil {
        return err
    }
    byte2,err := json.Marshal(from)
    if err != nil {
        return err
    }
    map1 := make(map[string]interface{})
    err = json.Unmarshal(byte1,&map1)
    if err != nil {
        return err
    }
    map2 := make(map[string]interface{})
    err = json.Unmarshal(byte2,&map2)
    if err != nil {
        return err
    }
    for k,v := range map2{
        map1[k] = v
    }
    byteDest,err := json.Marshal(map1)
    if err != nil {
        return err
    }
    err = json.Unmarshal(byteDest,to)
    return err
}

func main() {
    st1 := myStruct{
        Id:1,
        Name:"saky1",
        Addr:"addr1",
    }
    st2 := myStruct{
        Id:2,
        Name:"saky2",
        Age:20,
    }
    fmt.Println("befor merge: ",st1)
    MergeStruct(&st1,&st2)
    fmt.Println("after merge: ",st1)

}

输出:

befor merge:  {1 saky1 false 0 addr1}
after merge:  {2 saky2 false 20 addr1}