Golang - Creating a string from value in array of structs - go

I have a an array of structs that each have an id and a title.
What is the most efficient way of creating a comma separated list of ids from this array.
eg
Struct A - id: 1, title: ....
Struct B - id: 2, title: ....
Struct C - id: 3, title: ....
Need a string "1,2,3"

Iterate the array and append to a buffer.
package main
import (
"bytes"
"fmt"
"strconv"
)
type data struct {
id int
name string
}
var dataCollection = [...]data{data{1, "A"}, data{2, "B"}, data{3, "C"}}
func main() {
var csv bytes.Buffer
for index, strux := range dataCollection {
csv.WriteString(strconv.Itoa(strux.id))
if index < (len(dataCollection) - 1) {
csv.WriteString(",")
}
}
fmt.Printf("%s\n", csv.String())
}

Related

How to create the dynamic struct except using interface and Using spread operator

I am trying to create the new API with Go and Fiber framework. I am using MongoDB.
Please refer to my below structure
type Product struct {
Name string `json:"name"`
Code string `json:"code"`
Description string `json:"description"`
Brand string `json:"brand"`
Variants []map[string]string `json:"variants"`
Categories []int `json:"categories"`
}
The Variants field will have another set array of objects. But the fields will be dynamic. So I can't define the structure. Now this one I solved with []map[string][string].
My task here I have to form the CSV file with the below format.
[{
Name: "Rock",
Code: "RRR"
Description: "This is rock"
Brand: "R"
...variants
},
{
Name: "Rock",
Code: "RRR"
Description: "This is rock"
Brand: "R"
...variants
},
{
Name: "Rambo",
Code: "RAM"
Description: "This is Rambo"
Brand: "RA"
...variants
},
{
Name: "Rambo",
Code: "RAM"
Description: "This is Rambo"
Brand: "RA"
...variants
}]
Only variants will change for every row. Others will be remaining same
I formed the other fields except for the Variants. I can't create the struct because the data will be dynamic.
I have a lot of confusion
How to create the struct for dynamic fields
How to spread the Variants at the root level
I am from javascript. So, we used the spread operator. Here I am confused about how to do that in Golang.
Help me out, guys
#mkopriva is right
However you can use reflection to dynamically construct a new struct if you like
follow is a example:
package main
import (
"encoding/json"
"fmt"
"reflect"
)
type DynamicMap map[string]interface{}
type Product struct {
Name string `json:"name"`
Code string `json:"code"`
Description string `json:"description"`
Brand string `json:"brand"`
Variants map[string]string `json:"variants"`
Categories []int `json:"categories"`
}
func (j Product) MarshalJSON() ([]byte, error) {
m := DynamicMap{}
t := reflect.TypeOf(j)
v := reflect.ValueOf(j)
for i := 0; i < t.NumField(); i++ {
if t.Field(i).Tag.Get("json") == "variants" {
dyn := v.Field(i).Interface().(map[string]string)
for key, val := range dyn {
m[key] = val
}
} else if t.Field(i).Type.Kind() == reflect.String {
m[t.Field(i).Tag.Get("json")] = v.Field(i).String()
} else {
m[t.Field(i).Tag.Get("json")] = v.Field(i)
}
}
return json.Marshal(m)
}
func (j Product) UnmarshalJSON(data []byte) error {
fmt.Println("Unmarshal...")
return json.Unmarshal(data, &j)
}
func main() {
p := Product{Name: "aa", Variants: map[string]string{"a": "a", "b": "b"}}
marshal, err := json.Marshal(p)
if err != nil {
fmt.Printf("%v\n", err)
}
fmt.Printf("%s\n", marshal)
}
{"a":"a","b":"b","brand":"","categories":{},"code":"","description":"","name":"aa"}

unmarshal custom value map

I have created a custom type for a map. I would like to unmarshal an array
json response into the map. The key value of the map changes each time the response is received. The issue I have is the unmarshal function does not map correctly to the custom values.
type id map[string]yp
type yp struct {
f1 string
f2 int
}
func main() {
data := []byte("[{\"unique1\":{\"f1\":\"1\",\"f2\":\"2\"}},{\"unique2\":{\"f1\":\"4\",\"f2\":\"7\"}}]")
var i []id
json.Unmarshal(data,&i)
fmt.Printf("%v",i)
}
Since the source value for f2 is string, you need to add a field tag:
package main
import (
"encoding/json"
"fmt"
)
var data = []byte(`
[
{
"unique1": {"f1": "1", "f2": "2"}
}, {
"unique2": {"f1": "4", "f2": "7"}
}
]
`)
func main() {
var ids []map[string]struct {
F1 string
F2 int `json:"f2,string"`
}
json.Unmarshal(data, &ids)
// [map[unique1:{F1:1 F2:2}] map[unique2:{F1:4 F2:7}]]
fmt.Printf("%+v\n", ids)
}

Copy slice with reflect and unsafe package

I understand that usage of unsafe package of golang is unsafe, but I'm doing it only for education purposes.
The idea is to copy a slice field of structure, copy it with reflect package and unsafe.pointer, modify and replace the original slice with new one.
And it looks like new slice is created and has correct capacity/length, and it contains an instance of GoodForSale type. But all fields of that instance (Name, Price and Qnty) have wrong values. So I suppose that I'm doing something wrong with pointers and getting garbade data:
package main
import (
"fmt"
"reflect"
"unsafe"
)
type GoodForSale struct {
Name string
Price int
Qnty int
}
type Lead struct {
ID int
Name string
ContactName string
Budget int
Items []GoodForSale
DateTime string
Status int
Win bool
}
func main() {
lead := &Lead{
ID: 41,
Name: "Phone",
ContactName: "John Smith",
Budget: 30000,
Items: []GoodForSale{
{
Name: "Iphone 6",
Price: 100,
Qnty: 3,
},
},
DateTime: "12.08.2020 11:23:10",
Status: 150,
Win: false,
}
//Change Items
pt1 := unsafe.Pointer(&lead.Items)
copyItems := &reflect.SliceHeader{
Data: uintptr(pt1),
Cap: cap(lead.Items),
Len: len(lead.Items),
}
items := *(*[]GoodForSale)(unsafe.Pointer(copyItems))
fmt.Println(items[0].Name)
}
It looks like I'm missing something about how pointers work here. But how can I make this idea work correct?
Here is a go playground url: https://play.golang.org/p/SKtJWe9RVEU
The problem is here:
pt1 := unsafe.Pointer(&lead.Items) // pointer the slice, a.k.a "slice header"
copyItems := &reflect.SliceHeader{
Data: uintptr(pt1), // trying to use it as pointer to the actual data.
"Data" needs a pointer to where the actual data starts, but you're giving a pointer to the slice itself.
This is the correct way to get a pointer to the slice's data:
pt1 := unsafe.Pointer(&lead.Items[0]) // pointer to the first element referenced by the slice
Note that this will panic if len(lead.Items) == 0. In that case, you should use unsafe.Pointer(nil).
You could also get the data pointer (along with len and cap) from the original slice by casting it as a reflect.SliceHeader like this:
copyHeader := *(*reflect.SliceHeader)(unsafe.Pointer(&lead.Items))
items := *(*[]GoodForSale)(unsafe.Pointer(&copyHeader))
but at this point we've essentially just made a convoluted and "unsafe" version of this:
items := lead.Items
Based on the inputs from Hymns For Disco modifying your code by providing the element of slice instead of whole slice to the pointer.
package main
import (
"fmt"
"reflect"
"unsafe"
)
type GoodForSale struct {
Name string
Price int
Qnty int
}
type Lead struct {
ID int
Name string
ContactName string
Budget int
Items []GoodForSale
DateTime string
Status int
Win bool
}
func main() {
lead := &Lead{
ID: 41,
Name: "Phone",
ContactName: "John Smith",
Budget: 30000,
Items: []GoodForSale{
{
Name: "Iphone 6",
Price: 100,
Qnty: 3,
},
},
DateTime: "12.08.2020 11:23:10",
Status: 150,
Win: false,
}
pt1 := unsafe.Pointer(&lead.Items[0])
copyHeader := *(*reflect.SliceHeader)(unsafe.Pointer(&lead.Items))
items := *(*[]GoodForSale)(unsafe.Pointer(&copyHeader))
//items := *(*[]GoodForSale)(unsafe.Pointer(copyItems))
fmt.Println(pt1)
fmt.Println(items[0].Name)
}
Output:
0xc00000c080
Iphone 6

Reduce a large struct into a small one?

Say I make a call to an api, which returns a slice of structs, each with a load of methods and fields, but I only want to use one field per each element of the returned values. How can I do this?
For instance, I call out to an API, and it returns a slice of x elements, each which has 4 values and 13method, but I only want 1 value and 0 methods (the slice of fetus structs). How can I marshall this into my own struct? eg:
func GETApi() []fetus {
//doGet() returns a slice of persons, which are described below
a := doGet() // this gets many detailed persons as a slice, but I just want them as a slice of fetus
/*
type person struct {
id:
age:
height:
width:
}
func (a *person) GetHeight() int { ... }
func (a *person) GetWidth() int { ... }
func (a *person) GetLaught() int { ... }
// I want to return a slice of these ([]fetus)
type fetus struct {
id:
}
var f fetus
f := a // how can I condense said slice of persons into a slice of fetus
return f
*/
Perhaps something like this?
package main
type Person struct {
Id string
Name string
Age string
LotsOfOtherFields string
}
type Fetus struct {
Id string
}
func main() {
persons := []Person{
{Id: "a", Name: "John"},
{Id: "b", Name: "Steve"},
{Id: "c", Name: "Fred"},
}
fetuses := make([]Fetus, len(persons))
for i, p := range persons {
// Create a new fetus struct and pluck the ID from the person struct
fetuses[i] = Fetus{Id: p.Id}
}
}

error assigning values to struct within a struct golang

I came across this situation where I was trying to assign values to a struct within a struct. There is no compiler error, but it does panic when you run it. Does Go have a different way to handle this data structure?
package main
import (
"fmt"
)
type Label struct {
ID int
Labels []struct {
ID int
Name string
}
}
func main() {
l := Label{}
l.ID = 100
l.Labels[0].ID = 200
l.Labels[0].Name = "me"
fmt.Println(l.ID)
fmt.Println(l.Labels[0].ID)
fmt.Println(l.Labels[0].Name)
}
https://play.golang.org/p/IiuXpaDvF1W
Thanks in advance.
The default value for a slice is nil so it has no elements, and you cannot assign to index 0 because it doesn't exist yet.
You can use append to add a new element to that slice using:
l.Labels = append(l.Labels, struct{
ID int
Name string
}{
ID: 200,
Name: "me",
})
https://play.golang.org/p/uAWdQdh0Ov7
In addition, your use of an inline/anonymous struct here means you'll need to redeclare the type when you append. Consider adding another declared type:
type SubLabel struct {
ID int
Name string
}
type Label struct {
ID int
Labels []SubLabel
}
// ...
l.Labels = append(l.Labels, SubLabel{
ID: 200,
Name: "me",
})
https://play.golang.org/p/4idibGH6Wzd

Resources