Passing array of structs to a function in Go - go

I have an object parsed from some XML file. It has struct type like this
type Report struct {
Items []Item `xml:......`
AnotherItems []AnotherItem `xml:......`
}
type Item struct {
Name string
}
type AnotherItem struct {
Name string
}
func (Item *Item) Foo() bool {
//some code here
}
func (AnotherItem *AnotherItem) Foo() bool {
//another code here
}
For each item i have to do smth like this:
func main(){
//some funcs called to get the report object
doSmth(report.Items)
doSmth(report.AnotherItems)
}
func doSmth(items ????){
for _, item : range items {
item.Foo()
}
}
Since i have different items with same function i want to have only one doSmth so i can't do just doSmth(items []Item)
And the question is - what should i write instead of "????" to get this working?
The only way i made it to pass report.Items into doSmth() was
func doSmth(items interface{})
But it throws me an error "cannot range over items (type interface {})"
And if instead of iteration i just put smth like
func doSmth(items interface{}){
fmt.Println(items)
}
program print the list of my items

Replace ???? with []Item: Go Playground. It is just the same as how that variable report.Items is defined in struct Report.
Ok, yes that does change the question. The thing I thought of immediately was that you just needed to create an interface such as Itemer and have []Itemer in the function definition of doSmth:
type Itemer interface {
Foo() bool
}
func doSmth(items []Itemer) {
...
}
Unfortunately this does not seem to be possible. I then tried some other permutations including using interface{}, []interface{} as well as variadic functions and I couldn't get anything to work.
After doing some research it turns out that Go will not convert the type of slices. Even if each element of a slice is an instance of an interface such as Itemer or interface{} it still won't do it. I could not re-find the source but apparently This is due to the fact that a new slice would have to be created and each element would have to be type cast individually from the old slice to the new slice: https://stackoverflow.com/a/7975763/2325784.
To me this suggests that a function such as doSmth is not possible.
The only thing I could suggest is to restructure the code. If you are willing to post more information about Foo and doSmth I can try to help with that.

You need to use variadic function for this scope:
func main(){
//some funcs called to get the report object
doSmth(report.Items)
}
func doSmth(items ...report.Items){
for _, item := range items {
item.Foo()
}
}

EDIT: Nvm, this wont work. Forgot about the fact that go doesn't have inheritance.
You could convert the array into a slice, and make the function take a slice as a parameter instead.
names := []string{"leto", "paul", "teg"}
process(names[:])
func process(names []string) {
...
}

You can call like this type of array of struct values using basic array concepts :
In this example , []item should be called as item.Item[i].Unitprice
I hope u would got some idea
type UserItem struct {
UniqueID string `json:"uniqueID" bson:"uniqueID,omitempty"`
Item []Item
}
//Item : ""
type Item struct {
ProductName string `json:"productName" bson:"productName,omitempty"`
UnitPrice float64 `json:"unitPrice" bson:"unitPrice,omitempty"`
Quantity float64 `json:"quantity" bson:"quantity,omitempty"`
//main : ""
func main(){
for i := 0; i < len(item.Item); i++ {
SubTotal := item.Item[i].UnitPrice * item.Item[i].Quantity
TotalTax := (item.Item[i].TaxPercentage / 100)
TotalTax = SubTotal * TotalTax
Tax := SubTotal + TotalTax
fmt.Println(Tax)
}

Related

How should I write this function to return a pointer?

Assuming we have the following structures:
type BuyerData struct {
id int
balance float64
}
type AgentData struct {
id int
buyerData BuyerData
}
type GlobalData struct {
agents map[int]AgentData
}
if I wanted to define a "insert equivalent of Java's class method" for GlobalData to return a pointer to it's value buyerData on a given id, which would be a method with the following signature:
func (globalData *GlobalData) getBuyerData(id int) *BuyerData
What would be written inside it? I'm having troubles because it's giving all sorts of errors like cannot take the address of, invalid indirect of or does not support indexing...
This is what I currently have that does not generate any compiler exception:
func (globalData *GlobalData) getBuyerData(id int) *BuyerData {
var agents *map[int]AgentData = &(globalData.agents)
var agentData AgentData = (*agents)[id]
return &(agentData.buyerData)
}
But I, honestly, don't know what I'm doing or if this is the correct way of doing it.
Assuming you'd just like to return a pointer to the value in the map in a AgentData struct indexed by the argument of the function, you likely mean to say:
func (agentsGlobalData *AgentData) getBuyerData(id int) (*BuyerData, bool) {
buyer, has := agentsGlobalData.agents[id]
return &buyer, has
}
You might also consider storing pointers to AgentData in that map instead, making your struct:
type GlobalData struct {
agents map[int]*AgentData
}
But you haven't told us enough about your use case to recommend one or the other, perhaps.
(You say callers should be able to "write" to a BuyerData, but there are not any public fields in that struct...)
You need to say something like this:
func (globalData *GlobalData) GetBuyerData(id int) *BuyerData {
if agent, found := globalData.Agents[id]; found {
return &agent.BuyerData
}
return nil
}
See it here: https://play.golang.org/p/lwXn4D1mr3J

Golang update struct field inside function passed as interface

I am new to go (golang). That is why my question may be irrelevant (or impossible to answer).
I have created two structs. Both of these embed another struct. Now I want to update a field of the embedded struct inside a function.
package main
import (
"fmt"
"reflect"
"time"
)
type Model struct {
UpdatedAt time.Time
}
type Fruit struct {
Model
label string
}
type Animal struct {
Model
label string
}
func update(v interface{}) {
reflectType := reflect.TypeOf(v)
reflectKind := reflectType.Kind()
if reflectKind == reflect.Ptr {
reflectType = reflectType.Elem()
}
m := reflect.Zero(reflectType)
fmt.Println(m)
}
func main() {
apple := &Fruit{
label: "Apple",
}
tiger := &Animal{
label: "Tiger",
}
update(apple)
update(tiger)
fmt.Println(apple)
fmt.Println(tiger)
}
I wish to implement the update function so that it will put the current time in UpdatedAt of the passed struct. But I am not able to do this.
In this case, the field of Fruit and Animal is same: label. But it will not always be. Please keep that in mind when providing your suggestions.
Any guidance would be much appreciated.
I'd avoid reflect or interface{} if you're starting to learn go. Beginners usually fall back on them like a void * crutch. Try to use concrete types or well defined interfaces.
This should get you going:
type Timestamper interface {
Update()
UpdatedTime() time.Time
}
type Model struct {
updated time.Time
}
func (m *Model) Update() { m.updated = time.Now() }
func (m *Model) UpdatedTime() time.Time { return m.updated }
type Fruit struct {
Model
label string
}
type Animal struct {
Model
label string
}
// update will work with a `Model` `Animal` or `Fruit`
// as they all implement the `Timestamper` interface`
func update(v Timestamper) {
v.Update()
}
Playground: https://play.golang.org/p/27yDVLr-zqd
Assuming you want to achieve this via reflection: first of all, you have to pass a pointer to the struct. You're now passing a copy of the struct, so any modifications done in update will be done on the copy, not on the instance you passed in. Then, you can lookup the field UpdatedAt in the interface passed in, and set it.
That said, that's probably not the best way to do this. Another way of doing this without reflection is:
func update(in *Model) {
in.UpdatedAt = time.Now()
}
func main() {
apple := &Fruit{}
update(&apple.Model)
}
Or:
func (in *Model) update() {
in.UpdatedAt = time.Now()
}
func main() {
apple := &Fruit{}
apple.update()
}

Creating array of struct dynamically in golang

I try to create a generic function that accepts any struct value and create a array of that struct type. Here is the code I tried. But I get the error "t is not a type". How can I implement this.
type RegAppDB struct {
nm string
data []interface{}
}
func CreateRegTable(tbl string, rec interface{}) RegAppDB {
t := reflect.TypeOf(rec)
fmt.Println(t)
return RegAppDB{"log", []t}
}
Go does not support generics, and any attempt to do something like that is not going to work out well. In your specific case, there are a couple of key problems:
You cannot use a variable as a type. Go is compile-time static typed, so anything that gets type information at runtime (i.e. reflect.TypeOf) happens too late to use the way you're trying to do it.
Equally important, your struct's field is of type []interface{}, which means the only type you can use for that field is []interface{}. []string, for example, is a different type, and cannot be assigned to that field.
I took another route. Need some beautification. But this works. So now data is an array of interface and from calling function I pass pointer to structure variables to Save function.
type RegAppDB struct {
nm string
data []interface{}
cnt int
}
// CreateRegTable creates a data structure to hold the regression result
func CreateRegTable(tbl string) *RegAppDB {
return &RegAppDB{tbl, make([]interface{}, 20), 0}
}
// Save implements saving a record Regression application DB
func (rga *RegAppDB) Save(rec interface{}) error {
rga.data[rga.cnt] = rec
rga.cnt++
return nil
}
// Show implements showing the regression table
func (rga *RegAppDB) Show() error {
fmt.Println(rga.cnt)
for i := 0; i <= rga.cnt; i++ {
fmt.Println(rga.data[i])
}
return nil
}
// Compare compares two regression table for equality
func (rga *RegAppDB) Compare(rgt *RegAppDB) bool {
return reflect.DeepEqual(rga, rgt)
}
It cannot be done for a generic type. If there are a fixed number of possible types, then you can do something like the following:
type RegAppDB struct {
nm string
data interface{}
}
func CreateRegTable(rec interface{}) RegAppDB {
switch rec.(type) {
case int:
return &RegAppDB{"log", []int{}}
case string:
return &RegAppDB{"log", []string{}}
}
return nil
}
Note: Your data in RegAppDB should be of type interface{} since []int implements interface{} but not []interface{}

How to make parameter of function is a "generic" struct

For example, I want to write a method like this:
func parseData(rawData []json.RawMessage) []interface{} {
var migrations []interface{}
for _, migration := range rawData {
// this is an custom struct
command := UserCommand{}
json.Unmarshal(migration, &command)
migrations = append(migrations, command)
}
return migrations
}
The problem of this code is: If I don't want to parse UserCommand but any other such as ProductCommand, I must write the same code, only different at line: command := UserCommand{}. So my question is: how can I generic this code.
I have tried this solution but it doesn't work:
func parseData(rawData []json.RawMessage, class interface{}) []interface{} {
var migrations []interface{}
for _, migration := range rawData {
command := class
json.Unmarshal(migration, &command)
migrations = append(migrations, command)
}
return migrations
}
// then I call this method
parseData(data, UserCommand{})
But it doesn't work. It return array of map[string]interface{} How can I fix this.
Edit:
Here is some my defined struct
type UserCommand struct {
User string
Info string
}
type ProductCommand struct {
Name string
Quanlity int
}
// I want to be able to call this method
parseData(data, UserCommand{})
It is possible to support this style of "generic" signature by using Go's reflect package.
package main
import (
"encoding/json"
"fmt"
"reflect"
)
type UserCommand struct {
User string
Info string
}
type ProductCommand struct {
Name string
Quantity int
}
func parseData(rawData []json.RawMessage, class interface{}) []interface{} {
var parsed []interface{}
for _, elem := range rawData {
// Create a pointer to a new zero value of the same type as `class`.
command := reflect.New(reflect.TypeOf(class))
// Pass a pointer to the new value to `json.Unmarshal`.
json.Unmarshal(elem, command.Interface())
// Insert the pointed-to new value into the output slice.
parsed = append(parsed, command.Elem().Interface())
}
return parsed
}
func main() {
data := []json.RawMessage{
json.RawMessage(`{"User":"u1","Info":"i1"}`),
json.RawMessage(`{"User":"u2","Info":"i2"}`),
}
parsed := parseData(data, UserCommand{})
fmt.Printf("%#v\n", parsed)
data = []json.RawMessage{
json.RawMessage(`{"Name":"n1","Quantity":1}`),
json.RawMessage(`{"Name":"n2","Quantity":2}`),
}
parsed = parseData(data, ProductCommand{})
fmt.Printf("%#v\n", parsed)
}
The output shows that the first parseData call has parsed two UserCommand structs and the second call has parsed two ProductCommand structs.
[]interface {}{main.UserCommand{User:"u1", Info:"i1"}, main.UserCommand{User:"u2", Info:"i2"}}
[]interface {}{main.ProductCommand{Name:"n1", Quantity:1}, main.ProductCommand{Name:"n2", Quantity:2}}

Golang interface to struct

I have a function that has a parameter with the type interface{}, something like:
func LoadTemplate(templateData interface{}) {
In my case, templateData is a struct, but each time it has a different structure. I used the type "interface{}" because it allows me to send all kind of data.
I'm using this templateData to send the data to the template:
err := tmpl.ExecuteTemplate(w, baseTemplateName, templateData)
But now I want to append some new data and I don't know how to do it because the "interface" type doesn't allow me to add/append anything.
I tried to convert the interface to a struct, but I don't know how to append data to a struct with an unknown structure.
If I use the following function I can see the interface's data:
templateData = appendAssetsToTemplateData(templateData)
func appendAssetsToTemplateData(t interface{}) interface{} {
switch reflect.TypeOf(t).Kind() {
case reflect.Struct:
fmt.Println("struct")
s := reflect.ValueOf(t)
fmt.Println(s)
//create a new struct based on current interface data
}
return t
}
Any idea how can I append a child to the initial interface parameter (templateData)? Or how can I transform it to a struct or something else in order to append the new child/data?
Adrian is correct. To take it a step further, you can only do anything with interfaces if you know the type that implements that interface. The empty interface, interface{} isn't really an "anything" value like is commonly misunderstood; it is just an interface that is immediately satisfied by all types.
Therefore, you can only get values from it or create a new "interface" with added values by knowing the type satisfying the empty interface before and after the addition.
The closest you can come to doing what you want, given the static typing, is by embedding the before type in the after type, so that everything can still be accessed at the root of the after type. The following illustrates this.
https://play.golang.org/p/JdF7Uevlqp
package main
import (
"fmt"
)
type Before struct {
m map[string]string
}
type After struct {
Before
s []string
}
func contrivedAfter(b interface{}) interface{} {
return After{b.(Before), []string{"new value"}}
}
func main() {
b := Before{map[string]string{"some": "value"}}
a := contrivedAfter(b).(After)
fmt.Println(a.m)
fmt.Println(a.s)
}
Additionally, since the data you are passing to the template does not require you to specify the type, you could use an anonymous struct to accomplish something very similar.
https://play.golang.org/p/3KUfHULR84
package main
import (
"fmt"
)
type Before struct {
m map[string]string
}
func contrivedAfter(b interface{}) interface{} {
return struct{
Before
s []string
}{b.(Before), []string{"new value"}}
}
func main() {
b := Before{map[string]string{"some": "value"}}
a := contrivedAfter(b)
fmt.Println(a)
}
You can't append data arbitrarily to a struct; they're statically typed. You can only assign values to the fields defined for that specific struct type. Your best bet is probably to use a map instead of structs for this.
Not recommended, but you can create structs dynamically using the reflect package.
Here is an example:
package main
import (
"encoding/json"
"os"
"reflect"
)
type S struct {
Name string
}
type D struct {
Pants bool
}
func main() {
a := Combine(&S{"Bob"}, &D{true})
json.NewEncoder(os.Stderr).Encode(a)
}
func Combine(v ...interface{}) interface{} {
f := make([]reflect.StructField, len(v))
for i, u := range v {
f[i].Type = reflect.TypeOf(u)
f[i].Anonymous = true
}
r := reflect.New(reflect.StructOf(f)).Elem()
for i, u := range v {
r.Field(i).Set(reflect.ValueOf(u))
}
return r.Addr().Interface()
}
You could use something like the Combine function above to shmush any number of structs together. Unfortunately, from the documentation:
StructOf currently does not generate wrapper methods for embedded fields. This limitation may be lifted in a future version.
So your created struct won't inherit methods from the embedded types. Still, maybe it does what you need.
If you are just looking to convert your interface to struct, use this method.
type Customer struct {
Name string `json:"name"`
}
func main() {
// create a customer, add it to DTO object and marshal it
receivedData := somefunc() //returns interface
//Attempt to unmarshall our customer
receivedCustomer := getCustomerFromDTO(receivedData)
fmt.Println(receivedCustomer)
}
func getCustomerFromDTO(data interface{}) Customer {
m := data.(map[string]interface{})
customer := Customer{}
if name, ok := m["name"].(string); ok {
customer.Name = name
}
return customer
}

Resources