Adding anonymous struct element to slice - go

let's say I have a slice of anonymous structs
data := []struct{a string, b string}{}
Now, I would like to append a new item to this slice.
data = append(data, ???)
How do I do that? Any ideas?

Since you're using an anonymous struct, you have to again use an anonymous struct, with identical declaration, in the append statement:
data = append(data, struct{a string, b string}{a: "foo", b: "bar"})
Much easier would be to use a named type:
type myStruct struct {
a string
b string
}
data := []myStruct{}
data = append(data, myStruct{a: "foo", b: "bar"})

Actually, I found a way to add elements to array without repeated type declaration.
But it is dirty.
slice := []struct {
v, p string
}{{}} // here we init first element to copy it later
el := slice[0]
el2 := el // here we copy this element
el2.p = "1" // and fill it with data
el2.v = "2"
// repeat - copy el as match as you want
slice = append(slice[1:], el2 /* el3, el4 ...*/) // skip first, fake, element and add actual
Slice of pointers to struct is more conventional. At that case coping will slightly differ
slice := []*struct { ... }{{}}
el := slice[0]
el2 := *el
All this is far from any good practices. Use carefully.

Related

How to convert a slice of maps to a slice of structs with different properties

I am working with an api and I need to pass it a slice of structs.
I have a slice of maps so I need to convert it to a slice of structs.
package main
import "fmt"
func main() {
a := []map[string]interface{}{}
b := make(map[string]interface{})
c := make(map[string]interface{})
b["Prop1"] = "Foo"
b["Prop2"] = "Bar"
a = append(a, b)
c["Prop3"] = "Baz"
c["Prop4"] = "Foobar"
a = append(a, c)
fmt.Println(a)
}
[map[Prop1:Foo Prop2:Bar] map[Prop3:Baz Prop4:Foobar]]
so in this example, I have the slice of maps a, which contains b and c which are maps of strings with different keys.
I'm looking to convert a to a slice of structs where the first element is a struct with Prop1 and Prop2 as properties, and where the second element is a struct with Prop3 and Prop4 as properties.
Is this possible?
I've looked at https://github.com/mitchellh/mapstructure but I wasn't able to get it working for my use case. I've looked at this answer:
https://stackoverflow.com/a/26746461/3390419
which explains how to use the library:
mapstructure.Decode(myData, &result)
however this seems to assume that the struct of which result is an instance is predefined, whereas in my case the structure is dynamic.
What you can do is to first loop over each map individually, using the key-value pairs of each map you construct a corresponding slice of reflect.StructField values. Once you have such a slice ready you can pass it to reflect.StructOf, that will return a reflect.Type value that represents the dynamic struct type, you can then pass that to reflect.New to create a reflect.Value which will represent an instance of the dynamic struct (actually pointer to the struct).
E.g.
var result []any
for _, m := range a {
fields := make([]reflect.StructField, 0, len(m))
for k, v := range m {
f := reflect.StructField{
Name: k,
Type: reflect.TypeOf(v), // allow for other types, not just strings
}
fields = append(fields, f)
}
st := reflect.StructOf(fields) // new struct type
sv := reflect.New(st) // new struct value
for k, v := range m {
sv.Elem(). // dereference struct pointer
FieldByName(k). // get the relevant field
Set(reflect.ValueOf(v)) // set the value of the field
}
result = append(result, sv.Interface())
}
https://go.dev/play/p/NzHQzKwhwLH

How to assign to struct fields from an array of values in order?

I know you can create a struct with a literal, listing the fields in order:
type Foo struct {
A string
B string
C string
}
foo := Foo{ "foo", "bar", "baz" }
Is there any way to do the same thing dynamically? I have an array of values (actually an array of arrays) and I want to assign them to an array of structs in field order, and there are rather more than three fields. Is there a way to say "assign this struct's fields from this array of values in order"? I really don't want to write a bunch of structArray[i].field1 = dataArray[i][0]; structArray[i].field2 = dataArray[i][1], etc.
My thoughts so far have been to use reflect, which seems overkillish, or maybe to create an array of field names and build json strings and unmarshal them. Any better ideas?
With reflection you can write a function like this:
func populate(dst any, src any) {
v := reflect.ValueOf(dst)
if v.Type().Kind() != reflect.Pointer {
panic("dst must be a pointer")
}
v = v.Elem()
if v.Type().Kind() != reflect.Struct {
panic("dst must be a pointer to struct")
}
w := reflect.ValueOf(src)
if w.Type().Kind() != reflect.Slice {
panic("src must be a slice")
}
for i := 0; i < v.NumField(); i++ {
// in case you need to support source slices of arbitrary types
value := w.Index(i)
if value.Type().Kind() == reflect.Interface {
value = value.Elem()
}
v.Field(i).Set(value)
}
}
You must make sure that dst is addressable, hence pass a pointer to Foo into populate; and that the i-th element in the source slice is actually assignable to the corresponding i-th field in the struct.
The code above is in a quite simplified form. You can add additional checks to it, e.g. with CanAddr or AssignableTo, if you think callers may misbehave.
Call it like:
func main() {
f := Foo{}
populate(&f, []string{"foo", "bar", "baz"})
fmt.Println(f) // {foo bar baz}
}
Here's a playground that also shows that you can pass a slice of []any as the source slice, in case the struct fields aren't all the same type: https://go.dev/play/p/G8qjDCt79C7

Iterating slice struct within struct using reflection

I'm trying to achieve the following:
Use-case:
I have three structures, I need to compare 2 of those against one. (in the example described as: a & b need to be compared against full)
Reflection is used to loop over every field, retrieve the name of the field. And comparing the difference between a & full, b & full, storing the results in a shared structure.
If the field equals World, we know it's a slice struct:
I need to retrieve the first index of the Bar slice within the Foo structure.
Even though the variable is a slice, I know it will always have a length of 1 in this use-case.
When retrieved I need to loop over those fields, like what is happening in the previous if statement.
Example code:
type Foo struct {
Hello string
World []Bar
}
type Bar struct {
Fish string
}
type Result struct {
Field string
Correct_A bool
Distance_A int
Correct_B bool
Distance_B int
Result []Result
}
func compare_structs() {
var full, a, b Foo
// filling in all variables...
result := []Result{}
rfx_f := reflect.ValueOf(full)
rfx_a := reflect.ValueOf(a)
rfx_b := reflect.ValueOf(b)
type_result := rfx_f.Type()
for i := 0; i < rfx_f.NumField(); i++ {
tmp_res := Result{
Field: type_result.Field(i).Name,
}
if reflect.TypeOf(full).Field(i).Type.Kind() != reflect.Slice {
value := rfx_f.Field(i).Interface()
value_a := rfx_a.FieldByName(tmp_res.Field).Interface()
value_b := rfx_b.FieldByName(tmp_res.Field).Interface()
// functions to compare the values of this field
tmp_res.compare(value, value_a, value_b)
tmp_res.lev(value, value_a, value_b)
result = append(result, tmp_res)
} else if tmp_res.Field == "World" {
/*
I need to retrieve the first index of the Bar slice within the Foo structure.
Even though the variable is a slice, I know it will always have a length of 1 in this use-case.
When retrieved I need to loop over those fields, like what is happening in the previous if statement.
*/
}
}
}
You first need to get the field:
wordField:=rfx_f.Field(i)
which you know to be a slice, so you index it to get the first element
item:=wordField.Index(0)
This will panic if index is out of range.
Then you can iterate the fields:
for fieldIx:=0;fieldIx<item.NumField();fieldIx++ {
field:=item.Field(fieldIx)
}

Append content to slice into a nested struct does not work

I have two nested structs like this:
type Block struct {
ID string
Contents []string
}
type Package struct {
Name string
Blocks []Block
}
Original package (p) does not change when I try to append a new Content in a specific block.
for _, b := range p.Blocks {
if b.ID == "B1" {
fmt.Println("Adding a new content")
b.Contents = append(b.Contents, "c3")
}
}
Example:
https://play.golang.org/p/5hm6RjPFk8o
This is happening because this line:
for _, b := range p.Blocks {
creates a copy of each element in the slice, and in this case this means creating a copy of each Block in the slice. So when you then make the changes in the loop body, you are making them to the copy of the Block, instead of to the Block in the slice.
If you instead use the index to get a pointer to each Block, e.g.
for i := range p.Blocks {
b := &p.Blocks[i]
// modify b ...
}
it works as expected:
https://play.golang.org/p/h_nXEX9oWRT
Alternatively, you can make the changes to the copy (as in your original code), and then copy the modified value back to the slice:
for i, b := range p.Blocks {
// modify b ...
p.Blocks[i] = b
}
https://go.dev/play/p/kVHTk-OTyC3
Even further, you could instead store pointers to Block in the slice (instead of the Block themselves), in which case your loop would be making a copy of the pointer, which is a valid way to access the Block the original pointers points to:
https://go.dev/play/p/I9-EyV_iCNS
When you are looping over a slice, each of the individual values retrieved from the slice is a copy of the corresponding element in the slice. So to modify the element in the slice, instead of the copy, you can access the element directly using the indexing expression. Or you can use pointers. Note that pointers are also copied but the copied pointer will point to the same address as the element in the slice and therefore can be used to directly modify the same data.
You can use indexing:
for i := range p.Blocks {
if p.Blocks[i].ID == "B1" {
fmt.Println("Adding a new content")
p.Blocks[i].Contents = append(p.Blocks[i].Contents, "c3")
}
}
https://play.golang.org/p/di175k18YQ9
Or you can use pointers:
type Block struct {
ID string
Contents []string
}
type Package struct {
Name string
Blocks []*Block
}
for _, b := range p.Blocks {
if b.ID == "B1" {
fmt.Println("Adding a new content")
b.Contents = append(b.Contents, "c3")
}
}
https://play.golang.org/p/1RjWlCZkhYv

Go: append directly to slice found in a map

I wanted to create a map of slices where values are appended to the corresponding slice. However, when trying to append directly to the slice returned by accessing it (see comment below), it would not be stored, so I had to go with the long form access (line below the comment).
Why is it so? I expected the access to the map to return some sort of pointer, so in my mind mappedAminoAcid == aminoAcidsToCodons[aminoAcid]; clearly, I'm wrong.
Thanks!
aminoAcidsToCodons := map[rune][]string{}
for codon, aminoAcid := range utils.CodonsToAminoAcid {
mappedAminoAcid, ok := aminoAcidsToCodons[aminoAcid]
if ok {
// NOT WORKING: mappedAminoAcid = append(mappedAminoAcid, codon)
aminoAcidsToCodons[aminoAcid] = append(mappedAminoAcid, codon)
} else {
aminoAcidsToCodons[aminoAcid] = []string{codon}
}
}
append returns a new slice if the underlying array has to grow to accomodate the new element. So yes, you have to put the new slice back into the map. This is no different from how strings work, for instance:
var x map[string]string
x["a"] = "foo"
y := x["a"]
y = "bar"
// x["a"] is still "foo"
Since a nil slice is a perfectly fine first argument for append, you can simplify your code to:
aminoAcidsToCodons := map[rune][]string{}
for codon, aminoAcid := range utils.CodonsToAminoAcid {
aminoAcidsToCodons[aminoAcid] = append(aminoAcidsToCodons[aminoAcid], codon)
}

Resources