Creating a struct from array - go

I have an Employee struct which looks like this:
type Employee struct {
firstName string
lastName string
role string
}
I read from an Excel file a row, which has the corresponding columns in the same order.
From the excel file I created an array that holds this data, so the array looks like this:
[personFirstName, personLastName, personRole]
And I want to initialize my Employee struct with it.
But I can't figure out how, because I can't iterate over the Employee struct like it's an array.
I thought about doing it manually, like this:
e := {
firstName: array[0],
lastName: array[1],
role: array[2],
}
But I guess there should be a better way.
Any Ideas?

There's nothing wrong with the code in the question. Here's how to use the reflect API to accomplish the goal:
Export the field to make the fields settable through the reflect API.
type Employee struct {
FirstName string
LastName string
Role string
}
Create reflect value for an employee. The & and Elem()
shenanigans are required to get a settable value.
var e Employee
v := reflect.ValueOf(&e).Elem()
For each field, set the string:
for i := 0; i < v.NumField(); i++ {
v.Field(i).SetString(values[i])
}
Run an example on the Go Playground.
Put the code in a function if you need it in more than one place:
// setStrings sets the fields of the struct pointed
// to by ptr to the specified values.
func setStrings(ptr interface{}, values []string) {
v := reflect.ValueOf(ptr).Elem()
for i := 0; i < v.NumField(); i++ {
v.Field(i).SetString(values[i])
}
}
Call it like this:
values := []string{"Monica", "Smith", "Manager"}
var e Employee
setStrings(&e, values)
fmt.Printf("%+v\n", e) // prints {FirstName:Monica LastName:Smith Role:Manager}
Run an example on the playground.

Related

How to set struct field value in which is one of fields of another struct using reflect?

I'm writing a tool which can help me set any field value of an object, but there's a problem.
I have two structs Player & Card:
type Player struct {
Name string
IDCard Card
}
type Card struct {
ID string
}
there is the function
func setVal(data interface{}, fieldPath []string, val string) {
// ...
}
what the function supposed to do is to set data's specific field value to val.
For Example
p := Player{
Name: "Tsuru",
IDCard: Card{ID: "id"},
}
fieldPath_Name := []string{"Name"}
fieldPath_ID := []string{"IDCard", "ID"}
setVal(&p, fieldPath_Name, "Miku") // set p.Name to "Miku"
setVal(&p, fieldPath_ID, "newID") // set p.IDCard.ID to "newID"
means to set field Name to "Miku", and ID to "newID" in IDCard which is the field of object p.
My method is to recursively get the value and type of every field using golang reflect according to fieldPath.
Now the name setting can be successful, but how can I finish the ID setting? IDCard is a struct and is one of the fields of Player.
Thanks!
rv := reflect.ValueOf(&p).Elem()
rv.FieldByName("IDCard").FieldByName("ID").SetString("newID")
https://play.golang.org/p/yab71JV_z0j

Update the fields of one struct to another struct

I have a struct:
type Person struct {
FirstName string
LastName int
Age int
HairColor string
EyeColor string
Height string
}
And I have a function that takes in 2 parameters, and updates the first Person's fields to the second person's fields:
func updateFields(personA *Person, personB Person) {
personA.FirstName = personB.FirstName
personA.LastName = personB.LastName
// Don't want to change Age.
personA.HairColor = personB.HairColor
personA.EyeColor = personB.EyeColor
personA.Height = personB.Height
}
Instead of hard-coding the values to change, how can I loop through the fields and update the first Person to have the same values as the second Person except for the "Age" field?
You can just copy the entire personB into personA and make a backup of the Age field before:
func updateFields(personA *Person, personB Person) {
tempAge := personA.Age
*personA = personB
personA.Age = tempAge
}
For trivially copying every field, you can simply do something like *personA = personB. If you need to "not copy" just one specific field (same field every time), you could probably just save that field's value in a separate variable, copy everything with *personA = personB, and then copy the value back. But this is only useful for very specific situations. It wouldn't allow for example to have a dynamic set of fields not to copy.
If you want to do it with more flexibility, you can do it using reflection, sample code below.
Notice that there may be a few limitations. Notably, you can only set exported fields. Also, if you don't test for these limitations and accidentally try to set a field that's not settable, or with a value of type that's not assignable to that field, etc, the reflect package will happily panic. So it's wise to add a lot of checks before you actually .Set(...) the field.
import (
"fmt"
"reflect"
)
type Person struct {
FirstName string
LastName int
Age int
HairColor string
EyeColor string
Height string
}
func updateFields(personA *Person, personB Person) {
// .Elem() called to dereference the pointer
aVal := reflect.ValueOf(personA).Elem()
aTyp := aVal.Type()
// no .Elem() called here because it's not a pointer
bVal := reflect.ValueOf(personB)
for i := 0; i < aVal.NumField(); i++ {
// skip the "Age" field:
if aTyp.Field(i).Name == "Age" {
continue
}
// you might want to add some checks here,
// eg stuff like .CanSet(), to avoid panics
aVal.Field(i).Set(bVal.Field(i))
}
}
func main() {
b := Person{
FirstName: "Bruno",
LastName: 1,
Age: 2,
HairColor: "hello",
EyeColor: "world",
Height: "tall",
}
a := Person{}
fmt.Println(a)
updateFields(&a, b)
fmt.Println(a)
}

Get all the tags of hierarichal struct in Go

I want to translate some fields of my Rest response from English to Hindi language. I have few translator files where I have mapping of words from English to Hindi. The name of the file I want to provide via field tags.
So my struct will look something like this
type myResponse struct {
City string `translatorFile:"CityEToH"`
State string `translatorFile:"StateEToH"`
StationCode []string `translatorFile:"StationCodeEToH"`
InsideStruct insideStruct
}
type insideStruct struct {
trainName string `translatorFile:"TrainEToH"`
StartingPoint string `translatorFile:"StationCodeEToH"`
FinishPoint string `translatorFile:"StationCodeEToH"`
}
I want to write a common translator method that will take interface{} as the input parameter and will return an interface (after converting the input) as output. I have just started to learn Go and I am stuck with the implementation. I am not able to create a map kind of structure that will map the fieldName to the corresponding translator file Name.
I have tried reflect.typeOf(input), but with this I am not able to get the tags of insideStruct. This is just an example structure of payload, I could have 4-5 inherited level of struct as well.
Is there a way to get the fieldName, tags and fieldValue together. Or is there any other better way to implement this ?
Here's a function that walks through values and calls a function for each string with the associated struct tag:
func walkStrings(v reflect.Value, tag reflect.StructTag, fn func(reflect.Value, reflect.StructTag)) {
v = reflect.Indirect(v)
switch v.Kind() {
case reflect.Struct:
t := v.Type()
for i := 0; i < t.NumField(); i++ {
walkStrings(v.Field(i), t.Field(i).Tag, fn)
}
case reflect.Slice, reflect.Array:
if v.Type().Elem().Kind() == reflect.String {
for i := 0; i < v.Len(); i++ {
walkStrings(v.Index(i), tag, fn)
}
}
case reflect.String:
fn(v, tag)
}
}
The function fn can use Value.String to get the value as a string and Value.SetString to change the value. Use StructTag.Get to get the tag. An example function is:
func translate(v reflect.Value, tag reflect.StructTag) {
if !v.CanSet() {
// unexported fields cannot be set
return
}
file := tag.Get("translatorFile")
if file == "" {
return
}
v.SetString(translatStringWithFile(v.String(), file)
}
Call walkStrings with a reflect.Value of a struct pointer and the empty string as the tag:
v := myResponse{
City: "mycity",
StationCode: []string{"code1", "code2"},
InsideStruct: insideStruct{"trainname", "start", "finish"},
}
walkStrings(reflect.ValueOf(&v), "", translate)
Run it on the playground.

Range over a slice of a struct

I have a struct that contains a slice of a struct and I am trying to retrieve the data from the inner struct. Here is an example of the struct:
type Data struct {
Quotes []struct {
Direct bool `json:"Direct"`
Legs struct {
ID int `json:"Id"`
} `json:"Legs"`
}
}
From the above code I would like to retrieve the value in ID. Here is what I have tried already:
for _, v := range Data.Quotes.Legs {
fmt.Println(v.ID)
}
But I get the following error:
Has no field or method Legs
This works if I just want the value in Direct:
for _, v := range Data.Quotes {
fmt.Println(v.Direct)
}
Does anyone have suggestions on how to do this?
Data.Quotes.Legs is not an array. Data.Quotes is:
var data Data
...
for _, v := range data.Quotes {
fmt.Println(v.Legs.ID)
}

How to get property of struct by name?

I have this struct definition :
// Two columns, both strings.
type ExampleStructItem struct {
Firstname string
Surname string
}
and I have this slice of column names :
columns := []string{"Firstname", "Surname"}
and I am essentially trying to loop through my slice of column names, and then perform reflection on the corresponding struct to get information about the properties, such as their "Kind" etc.
Just use Type.FieldByName()
var ex ExampleStructItem
t := reflect.TypeOf(ex)
for _, name := range columns {
field, ok := t.FieldByName(name)
if ok {
k := field.Type.Kind()
} else {
// error handling
}
}
Playground

Resources