Range over a slice of a struct - go

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)
}

Related

Extract tags from Go struct as a reflect.Value

I'm trying to extract tags from a certain Value which is a struct. I am able to get the fields of the struct but unable to extract the tags. What am I doing wrong here? I've tried many different ways (using reflect.Type, interface{} etc) but all failed.
type House struct {
Room string
Humans Human
}
type Human struct {
Name string `anonymize:"true"` // Unable to get this tag
Body string
Tail string `anonymize:"true"` // Unable to get this tag
}
func printStructTags(f reflect.Value) { // f is of struct type `human`
for i := 0; i < f.NumField(); i++ {
fmt.Printf("Tags are %s\n", reflect.TypeOf(f).Field(i).Tag) // TAGS AREN'T PRINTED
}
}
The reason I use reflect.Value as the parameter is because this function is called from another function in the following manner
var payload interface{}
payload = &House{}
// Setup complete
v := reflect.ValueOf(payload).Elem()
for j := 0; j < v.NumField(); j++ { // Go through all fields of payload
f := v.Field(j)
if f.Kind().String() == "struct" {
printStructTags(f)
}
}
Any insights would be extremely valuable
When you call reflect.TypeOf(f) you get the type of f, which is already a reflect.Value.
Use the Type() func of this f to get the type and do the Field check on it:
func printStructTags(f reflect.Value) { // f is of struct type `human`
for i := 0; i < f.NumField(); i++ {
fmt.Printf("Tags are %s\n", f.Type().Field(i).Tag)
}
}
However, it's easier to check for a tag and get its value with f.Type().Field(i).Tag.Get("anonymize"). This way you directly get the assigned value, for example true in your case and an empty string if the tag doesn't exist.

How to filter slice of struct using filter parameter in GO

I am new to GOlang, I trying to filter slice of struct in GO using struct which contain some parameter for filter it. I am trying to do below things but its not work for me. In that code I have filter function which take slice of struct i.e GRN which need to filter using FilterParameter struct.
My Struct which need to filer
type GRN struct {
DocType string `json:"docType"`
GRNNO string `json:"GRNNO"`
PRID string `json:"PRID"`
PRODUCE string `json:"PRODUCE"`
VARIETY string `json:"VARIETY"`
}
manyGRNUsers := []GRN{{"GRNAsset","GRN0000109","PRFAINAPKR0086","Sweetcorn","CP2"},
{"GRNAsset","GRN0000110","PRFAINAPKR0087","Sweetcorn","CP1",},
{"GRNAsset","GRN0000112","PRFAINAPKR0087", "Sweetcorn","CP2",},
{"GRNAsset","GRN0000113","PRFAINAPKR0089","Apple","Gala",}}
Struct which will used for filter parameter.here one thing the field in FilterParameter is dyanamic as above GRN struct value. it could me more then two field but less than or equal to GRN struct field
type FilterParameter struct {
PRODUCE string `json:"PRODUCE"`
VARIETY string `json:"VARIETY"`
}
manyFilterParameter := []FilterParameter{{"Sweetcorn","CP2" }
I tried below function but its not work for me as I can do only with one field from FilterParameter i.e PRODUCE only and its not dynamic
func filter(fu []GRN, su []FilterParameter) (out []GRN) {
f := make(map[string]struct{}, len(su))
for _, u := range su {
f[u.PRODUCE] = struct{}{}
}
for _, u := range fu {
if _, ok := f[u.PRODUCE]; ok {
out = append(out, u)
}
}
return
}
You can't really do what you're trying to do, as it's not possible to iterate the fields of a struct (maybe with reflection). For another approach, you could use maps instead of structs, or even easier, if you can just explicitly use the fields you care about in the function parameters:
package main
import "fmt"
type GRN struct { DocType, GRNNO, PRID, PRODUCE, VARIETY string }
func filter(in []GRN, produce, variety string) []GRN {
var out []GRN
for _, each := range in {
if each.PRODUCE == produce && each.VARIETY == variety {
out = append(out, each)
}
}
return out
}
func main() {
manyGRNUsers := []GRN{
{"GRNAsset","GRN0000109","PRFAINAPKR0086","Sweetcorn","CP2"},
{"GRNAsset","GRN0000110","PRFAINAPKR0087","Sweetcorn","CP1"},
{"GRNAsset","GRN0000112","PRFAINAPKR0087", "Sweetcorn","CP2"},
{"GRNAsset","GRN0000113","PRFAINAPKR0089","Apple","Gala"},
}
out := filter(manyGRNUsers, "Sweetcorn", "CP2")
fmt.Println(out)
}

function with receiver that iterates through some field of struct in go

i'm a Golang newbie trying to build a simple CLI. I'm converting an API call payload into an struct and want to format some information from this struct into a nice printable string. One of the informations i want to print is from an array of structs, like in this example:
type Pokemon struct {
Abilities []struct {
Ability struct {
Name string `json:"name"`
URL string `json:"url"`
} `json:"ability"`
IsHidden bool `json:"is_hidden"`
Slot int `json:"slot"`
} `json:"abilities"`
Height int `json:"height"`
ID int `json:"id"`
Name string `json:"name"`
Types []struct {
Slot int `json:"slot"`
Type struct {
Name string `json:"name"`
URL string `json:"url"`
} `json:"type"`
} `json:"types"`
Weight int `json:"weight"`
}
}
I'm trying to write a generic receptor function that iterates over some fields that are struct arrays and join its fields in a string. I can do a function that iterate specifically over some field like this:
func (p Pokemon) stringifyPokemonAbilities() string {
var listOfAbilities []string
for _, ability := range p.Abilities {
listOfAbilities = append(listOfAbilities, ability.Ability.Name)
}
return strings.Join(listOfAbilities[:], " / ")
}
Returning e.g. synchronize / inner-focus
Working like this, i'll have to write almost the same function to the Type field. My question is, how to make this function more generic, accepting a field and iterating on it. Any thoughts?
You could do that using relfect. There is a well written tutorial, which you could have a look at.
According to the struct you give, I have written a demo. The main idea is
find the struct filed by name, then iterator the slice, find the name in the struct.
You could get the answer using p.stringifyPokemon("Types") or p.stringifyPokemon("Abilities") now.
func (p Pokemon) stringifyPokemon(field string) string {
value := reflect.ValueOf(p)
struct1 := value.FieldByName(field)
if !struct1.IsValid() {
return ""
}
if struct1.Type().Kind() != reflect.Slice {
return ""
}
ans := make([]string, 0)
for i := 0; i < struct1.Len(); i++ {
slice1 := struct1.Index(i)
if slice1.Type().Kind() != reflect.Struct {
continue
}
for j := 0; j < slice1.NumField(); j++ {
struct2 := slice1.Field(j)
if struct2.Type().Kind() != reflect.Struct {
continue
}
name := struct2.FieldByName("Name")
if name.Kind() != reflect.String {
continue
}
ans = append(ans, name.String())
}
}
return strings.Join(ans[:], " / ")
}

How to reduce time complexity while mapping structs with sub-slices in go?

Let's say there's the following Unload struct, which comes as a single element response from microservice A, and where each Item initially has an empty Units slice:
type Unload struct {
UnloadCode string
Orders []Order
}
type Order struct {
OrderCode string
Items []Item
}
type Item struct {
ItemCode string
Units []string
}
And also a ItemUnit struct which comes as a slice response from microservice B:
type ItemUnit struct {
ItemCode string
Units []Unit
}
type Unit struct {
UnitName string
}
And we need to populate the Item, Units slice with it's corresponding UnitName values, based on similar ItemCodes on both sides.
I've managed to come up with the following solution, for solving this issue:
for orderIndex, order := range unload.Orders {
for itemIndex, item := range order.Items {
for _, itemUnit := range itemUnits {
if item.ItemCode == itemUnit.ItemCode {
for _, unit := range itemUnit.Units {
unload.Orders[orderIndex].Items[itemIndex].Units = append(unload.Orders[orderIndex].Items[itemIndex].Units, unit.UnitName)
}
}
}
}
}
I'm not a go expert myself, but it seems to me that this solution comes with a very high time complexity cost. Would there be any other, more elegant and possibly with a smaller time complexity, way of solving this issue?
*Keep in mind that I can't change the structure of any of my structs.
First, create a map for ItemUnit where itemUnit.ItemCode as key and slice of UnitName as value
itemUnitmap := make(map[string][]string)
for _, itemUnit := range itemUnits {
var units []string
for _, unit := range itemUnit.Units {
units = append(units, unit.UnitName)
}
itemUnitmap[itemUnit.ItemCode] = units
}
Then use map to get slice of UnitName using item.ItemCode. Add slice into Item.Units using variadic function
for orderIndex, order := range unload.Orders {
for itemIndex, item := range order.Items {
if units, ok := itemUnitmap[item.ItemCode]; ok {
unload.Orders[orderIndex].Items[itemIndex].Units = append(unload.Orders[orderIndex].Items[itemIndex].Units, units...)// variadic function used to append slice into slice
}
}
}

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