Donutloop
Donutloop

Reputation: 451

Convert a type struct A to B

I want to convert Struct A to B but I didn’t found a simple and good approach for this problem

   type Bob struct {
      Name string 
      Age int 
   }

   type Mark struct {
      Name string 
      Age int 
      HairColor string 
   }

any ideas? I don’t want to write a assign statement for all properties like

Mark{
Name:bob.Name, 
Age: bob.Age,
}

Because this approach get's very tedious if a Struct A and B have a lot of properties like 20

The solution should also handle deeply nested structs

Edit: sometimes the underlying structure isn’t equal of both structs like A has more properties like B or vice versa

Upvotes: 3

Views: 4199

Answers (4)

Laevus Dexter
Laevus Dexter

Reputation: 502

If first fields are the same and you aren't squeamish about using unsafe, then here's the fastest one line solution:

https://play.golang.org/p/gqlq-vLsLXE

Upvotes: 0

Andy Schweig
Andy Schweig

Reputation: 6749

Another possibility is to put the common fields in a separate struct and embed that in the other structs:

type Person struct {
    Name string
    Age  int
}

type Bob struct {
    Person
}

type Mark struct {
    Person
    HairColor string
}

Then you can create a Mark object with

Mark{
    Person: bob.Person,
    // other initializations
}

Upvotes: 2

Thundercat
Thundercat

Reputation: 120960

Because Mark and Bob have the same underlying type, a conversion can be used to to convert between the two types. Here's how to convert a Bob to a Mark:

b := Bob{Name: "Bob", Age: 22}
m := Mark(b)

Run it on the playground.

Edit: OP changed the question. Updated answer follows.

Use the reflect package to copy the subset of matching fields from one struct to the other:

// copyCommonFields copies the common fields from the struct
// pointed to srcp to the struct pointed to by destp.
func copyCommonFields(destp, srcp interface{}) {
    destv := reflect.ValueOf(destp).Elem()
    srcv := reflect.ValueOf(srcp).Elem()

    destt := destv.Type()
    for i := 0; i < destt.NumField(); i++ {
        sf := destt.Field(i)
        v := srcv.FieldByName(sf.Name)
        if !v.IsValid() || !v.Type().AssignableTo(sf.Type) {
            continue
        }
        destv.Field(i).Set(v)
    }
}

Use it like this:

b := Bob{Name: "Bob", Age: 22, ShoeSize: 9}
var m Mark
copyCommonFields(&m, &b)

Run it on the playground.

Upvotes: 3

see sharper
see sharper

Reputation: 12035

The general, low-level way to achieve this in Go is by using reflection. You can look up the names of the fields and set the values appropriately. However I would recommend using a library, such as https://github.com/jinzhu/copier for deep copying. Otherwise you'll find it gets quite hairy with different types such as pointers and slices.

You can simply do: copier.Copy(&mark, &bob) (where mark and bob are instances of Mark and Bob structs, obviously).

Upvotes: 1

Related Questions