Mapping between structs in a loop - loops

I have a function that does some mapping between 2 structs:
Struct1 => Struct2
where Struct1 is as follows:
type Struct1 struct {
A Transaction `json:"transaction"`
B string `json:"name"`
...
}
whereas Struct2 looks like this:
type Struct2 struct {
C AnotherTransaction `json:"transaction"`
D string `json:"name"`
...
}
I have a function that maps the "inner" type Transaction => AnotherTransaction, but the issue I have is that there is an outer Struct, named Struct3 for convenience, that is as follows:
type Struct3 struct {
Failed []Struct2 `json:"failed"` // a list of transactions
Success []Struct2 `json:"success"`
}
func mapTo(st3 Struct3) Struct1 {
st1 := Transaction{}
// the mapping between A => C is quite lengthy
st1.someField = st3.struct2.anotherField
return st1 // now mapped
}
My issue is that from Struct3 I need to access each element of Struct2 and fire up the mapping function above, but I am not sure how to go about it. How can I loop through each element of []Struct2 append each element and return Struct3 now populated with the mapping from mapTo()?

You can update the map function's argument to struct2 and loop through the struct3's fields of array from main function and send each of them to the toMap function.
func main() {
type Struct3 struct {
Failed []Struct2 `json:"failed"` // a list of transactions
Success []Struct2 `json:"success"`
}
s3 := &Struct3{
Failed: make([]Struct2, 0),
Success: make([]Struct2, 0),
}
for i := range s3.Success {
// do something with the result value
_ = toMap(s3.Success[i])
}
for i := range s3.Failed {
// do something with the result value
_ = toMap(s3.Failed[i])
}
}
func mapTo(st2 Struct2) Struct1 {
st1 := Transaction{}
// the mapping between A => C is quite lengthy
st1.someField = st2.anotherField
return st1 // now mapped
}

Related

Golang array in struct issue

I have problems using a array of structs inside another struct. The issue is that when I fill the structure with data from JSON data in the function below, it is filled correctly. When I directly try to access the data outside of the loop within a new loop the data is not there. So I guess that I'm filling the copied data structure instead of the reference to it so it's only valid inside the first loop. Though I have tried to allocate memory for it and still the same issue.
I guess that I failed somewhere and need directions. See some comments below in the code snippet.
type Spaces struct {
Items []*Space `json:"items"`
}
type Space struct {
Id string `json:"id"`
Messages []Message `json:"items"`
}
type Messages struct {
Items []Message `json:"items"`
}
// spaces are marshalled first so that there is a array of spaces
// with Id set. Then the function below is called.
func FillSpaces(space_id string) {
for _,s := range spaces.Items {
if s.Id == space_id {
// I tried to allocate with: s.Messages = &Messages{} without any change.
json.Unmarshal(f, &s) // f is JSON data
fmt.Printf(" %s := %v\n", s.Id, len(s.Messages))) // SomeId := X messages (everything seems fine!)
break
}
}
// Why is the messages array empty here when it was not empty above?
for _,s := range spaces.Items {
if s.Id == space_id {
fmt.Printf("%v", len(s.Messages))) // Length is 0!?
}
}
}
The application is unmarshaling to the variable s defined in the loop. Unmarshal to the slice element instead:
for i, s := range spaces.Items {
if s.Id == space_id {
err := json.Unmarshal(f, &spaces.Items[i]) // <-- pass pointer to element
if err != nil {
// handle error
}
break
}
}

I cannot modify the item after appending to array

I'm developing a multiplayer api for Unity Engine. I have Room and RoomManager struct. When I want create room, I set room name, id (all of variable), after that I append this new room to allRoom array in RoomManager struct. After that when I modified to room variable, I can't change any variable. I dont know why ?
Here is my structs and methods :
RoomManager struct
type RoomManager struct {
allRooms []Room
roomCounter int
}
Room struct
type Room struct {
tag string
name string
password string
id int
cappacity int
maxVariableCount int
userList []User
roomVariables []RoomVariable
extensionHandlers []ExtensionRequest
InitializeMethod roomInitializeFunc
}
Room Create Method
func (roomManager *RoomManager) CreateRoom(settings RoomSettings, arwServer *ARWServer) *Room {
var newRoom Room
newRoom.InitializeMethod = settings.InitializeMethod
newRoom.name = settings.name
newRoom.password = settings.password
newRoom.tag = settings.tag
newRoom.cappacity = settings.cappacity
newRoom.maxVariableCount = settings.maxRoomVariableCount
newRoom.id = roomManager.roomCounter
roomManager.roomCounter++
newRoom.userList = make([]User, 0, newRoom.cappacity)
newRoom.roomVariables = make([]RoomVariable, 0, newRoom.maxVariableCount)
if newRoom.InitializeMethod != nil {
newRoom.InitializeMethod(arwServer, &newRoom)
}
roomManager.allRooms = append(roomManager.allRooms, newRoom)
return &newRoom
}
Add User To Room
func (room *Room) AddUserToRoom(arwServer *ARWServer, u User) {
room.userList = append(room.userList, u)
var arwObj ARWObject
arwObj.requestName = Join_Room
arwObj.eventParams.PutString("RoomName", room.name)
arwObj.eventParams.PutString("RoomTag", room.tag)
arwObj.eventParams.PutInt("RoomId", room.id)
arwObj.eventParams.PutInt("RoomCappacity", room.cappacity)
usersData := ""
for ii := 0; ii < len(room.userList); ii++ {
if room.userList[ii].name != u.name {
usersData += room.userList[ii].GetDataForOtherUser(u) + "''"
}
}
usersData = strings.TrimRight(usersData, "''")
arwObj.eventParams.PutString("Users", usersData)
arwServer.SendRequestToUser(u, arwObj)
var arwObjforTheOthers ARWObject
arwObjforTheOthers.requestName = User_Enter_Room
arwObjforTheOthers.eventParams.PutString("RoomName", room.name)
arwObjforTheOthers.eventParams.PutString("userName", u.name)
arwObjforTheOthers.eventParams.PutInt("userId", u.id)
arwObjforTheOthers.eventParams.PutString("isMe", "false")
room.SendRequestAllUserWithoutMe(*arwServer, arwObjforTheOthers, u)
fmt.Println("User join Room - User Name : ", u.name+" Room : "+u.lastRoom.name)
}
Your issue is that your structs have non-pointer slices. In all of your structs define your slices as slices of pointers like so
Rooms []*Room
You also need to define your functions to take pointer values, like so
func(room *Room) {}
To elaborate. Go is pass-by-value. Anytime you pull something out of one of your original slices and pass it to one of your functions, it gets a copy. Using pointers modifies the actual value in the slice.
See this example. https://play.golang.org/p/ZThHrP0pds
package main
import (
"fmt"
)
type Thing struct {
Name string
}
func main() {
things := []Thing{}
thing := Thing{"thing1"}
// add to slice
// note that this is a function call
things = append(things, thing)
// attempt to change
thing.Name = "thing2"
fmt.Println(things[0].Name) // prints thing1
fmt.Println(thing.Name) // prints thing2
fmt.Println("------")
// try again
thing3 := things[0]
thing3.Name = "thing3"
// fail again
fmt.Println(things[0].Name) // prints thing1
fmt.Println(thing3.Name) // prints thing3
fmt.Println("------")
// do this instead
betterThings := []*Thing{} // slice of pointers
betterThing := &Thing{"thing2"} // betterThing is of type *Thing
// add to slice
betterThings = append(betterThings, betterThing)
// successfully change
betterThing.Name = "thing2"
fmt.Println(betterThings[0].Name) // prints thing2
fmt.Println(betterThing.Name) // prints thing2
}

Append to array of struct inside a struct

type Customer struct{
UID string
Name string
Contact []ContactInfo
}
type ContactInfo struct {
Number int
}
There can be multiple customers and each customer can have multiple contact numbers.
I was using the following approach to append the Contact array of struct for a particular user as follows.
customer := &Customer{}
customer.UID = args[0]
customer.Name = args[1]
c:= &ContactInfo{}
c.Number = args[2]
customer.Contact= append(customer.Contact, *c)
But this approach isn't working as I only get the latest Contact and not the array of Contact in output.
The observed behaviour is possible when you are passing object by value into a function or method. The modifications inside those functions will not be visible outside.
See the code below
package main
import (
"fmt"
)
type X struct{
Y []string
}
func modify(x X) {
x.Y = append(x.Y, "modify")
}
func (x X) change() {
x.Y = append(x.Y, "modify")
}
func main() {
x := X{}
modify(x)
x.change()
fmt.Printf("%+v\n", x)
}

How do I STORE different structs in a slice or a struct in Go (Not embedding)

I am new to Golang, after I took a tour of A Tour of Go, I'm trying to make my own thing.
What I want
I want to put different types of the structs into a single slice (or struct?),
so I can use a for loop to pass each struct to a function.
For example
In PHP I can store my classes in an array and pass each of them to foobar() like this:
$classes = [$A, $B, $C, $D]; // $A, $B, $C, $D are classes (called `struct` in Golang).
foreach ($classes as $class)
foobar($class);
What I tried
I tried to do the same in Golang, and I hope it looks like this:
A{B{}, C{}, D{}}
Since I failed on using slice, I decided to use struct to hold my structs:
type A struct {
B
C
D
}
type B struct {
Date string
}
type C struct {
Date string
}
type D struct {
Date string
}
func main() {
// Using reflect to loop the A struct:
// http://stackoverflow.com/questions/18926303/iterate-through-a-struct-in-go
v := reflect.ValueOf(A{})
for i := 0; i < v.NumField(); i++ {
foobar(v.Field(i).Interface()) // Passing each struct to the `foobar()` function
}
}
But it seems like I'm actually doing embedding instead of storing them, any suggestions please?
I think interface{} is what you're after. For example like this:
type A struct {
data string
}
type B struct {
data int
}
func printData(s interface{}) {
switch s.(type) {
case A:
fmt.Printf("A: %s\n", s.(A).data)
case B:
fmt.Printf("B: %d\n", s.(B).data)
}
}
func main() {
classes := []interface{}{A{data: "first"}, B{data: 2}, A{data: "third"}}
for _, c := range classes {
printData(c)
}
}
This is probably as close as you can get to "duck typing" in go.
Though if your structs are really that similar, you might better off defining a common interface instead and implementing that interface for each struct. Here is same example using interfaces:
type DataGetter interface {
getDate() string
}
type A struct {
data string
}
func (a A) getDate() string {
return a.data
}
type B struct {
data int
}
func (b B) getDate() string {
return fmt.Sprintf("%d", b.data)
}
func printData(s DataGetter) {
fmt.Printf("%s\n", s.getDate())
}
func main() {
classes := []DataGetter{A{data: "first"}, B{data: 2}, A{data: "third"}}
for _, c := range classes {
printData(c)
}
}

How to set new value to struct member of explicit type from interface{} value (reflection)? Golang

I want to understand some subtle moments of using reflect package. Please, see example below, it describes better what I want to know:
type Robot struct {
id int
model string
}
func change(i interface{}, fields ...string) {
v := reflect.ValueOf(i).Elem()
// here I emulate function by slice that could return any value,
// so here I need to check if I can store incoming values to existing struct
returns := []interface{}{100, "Something"}
for i, name := range fields {
x := reflect.ValueOf(&returns[i]).Elem()
//check if value representing x is the same of struct member
v.FieldByName(name).Set(x)
// ^ here I want to store 100 to Robot.id when i = 0,
// and "Something" to Robot.model when i = 1
}
}
func main() {
robot := &Robot{id: 1, model: "T310"}
change(robot, "model", "id")
// now robot become as follows: &Robot{100, "Something"}
}
Why does it need for?
// It is need for retrieving values from sql DB into struct members
// (only for training purposes :))
// Example:
f := func(q string, structs interface{}, fields ...string) {
rows, _ := db.Query(q)
for i := 0; rows.Next(); i++ {
rows.Scan(&structs[i])
// very dirty here! it's hard to understand how to implement it
}
}
var robots = []*Robot
f("select id, model from robots", robots, "id", "model")
// now each member of robots var should contain values from DB
I tried to be descriptive and explain as short as possible. I hope you understand me..
You can only set exported fields via reflection, so capitalize those first. Otherwise, if you're counting on positional values, make sure they are properly aligned.
Something like this for example: http://play.golang.org/p/ItnjwwJnxe
type Robot struct {
ID int
Model string
}
func change(i interface{}, fields ...string) {
returns := []interface{}{100, "Something"}
v := reflect.ValueOf(i).Elem()
for i, name := range fields {
val := reflect.ValueOf(returns[i])
v.FieldByName(name).Set(val)
}
}
func main() {
robot := &Robot{ID: 1, Model: "T310"}
fmt.Println(robot)
change(robot, "ID", "Model")
fmt.Println(robot)
}

Resources