Create map or objects from struct with additional properties as key - dictionary

i've struct which build like following
type RTB struct {
ID string
Modules []*Modules
Req []*Req
}
Now inside module I've the following
type Modules struct {
Name string
Type string
Path string
Id string
}
Now I've the object of RTB in the memory and I want to create map (that I can loop on it si object which will be like following:
NewObject {
Type string//the value from the module struct
Modules []*Modules // From the rtb struct
}
of course I can loop on it (if there is not more elegant way...) and create new struct (like the new object) and fill the data from both structs, but there is better way in Golang like map to store this data?

You have to use a loop to go over the modules and build a map from it. No easier way. If you need this functionality in multiple places, put it into a utility function and call that wherever needed.
Example building the map:
rtb := &RTB{
Modules: []*Modules{
{Name: "n1", Type: "t1"},
{Name: "n2", Type: "t1"},
{Name: "n3", Type: "t2"},
},
}
m := map[string][]*Modules{}
for _, mod := range rtb.Modules {
m[mod.Type] = append(m[mod.Type], mod)
}
// Verify result (JSON for "nice" outpout):
fmt.Println(json.NewEncoder(os.Stdout).Encode(m))
Output:
{"t1":[{"Name":"n1","Type":"t1"},{"Name":"n2","Type":"t1"}],"t2":[{"Name":"n3","Type":"t2"}]}
<nil>
If you want a slice of NewObject instead of the map, you can build that like this:
// m is the map built above.
var newObjs []*NewObject
for k, v := range m {
newObjs = append(newObjs, &NewObject{
Type: k,
Modules: v,
})
}
fmt.Println(json.NewEncoder(os.Stdout).Encode(newObjs))
Output:
[{"Type":"t1","Modules":[{"Name":"n1","Type":"t1"},{"Name":"n2","Type":"t1"}]},{"Type":"t2","Modules":[{"Name":"n3","Type":"t2"}]}]
<nil>
Try the examples on the Go Playground.

Related

How does value change in Golang Map of interface

This is the code base -
https://go.dev/play/p/BeDOUZ9QhaG
Output -
map[something:map[ACM:34.12 age:12 dune:dune]]
How does changing values in t variable affect in x?
package main
import "fmt"
func main() {
x: = make(map[string] interface {}, 10)
x["something"] = map[string] interface {} {
"dune": "dune", "age": 12
}
t: = x["something"].(map[string] interface {})
t["ACM"] = 34.12
fmt.Println(x)
}
Map types are reference types, like pointers or slices,
so this line
t := x["something"].(map[string]interface{}) t["ACM"] = 34.12 fmt.Println(x) }
is just a shallow copy creating alias for the existing map you created above in x variable ,so they are pointing to same memory address where the original map you created exists.
See for reference -https://go.dev/blog/maps

How to use struct keys in a map?

I am implementing some DB logic without using an ORM.
Can I somehow create a mapping between struct keys and db enums?
type Message struct {
SomeKey string
SomeOtherKey string
}
MessageToDBEnum: = map[ ? MessageKey] string {
SomeKey: "some_key",
SomeOtherKey: "some_other_key"
}
can I later dynamically use the map key (eg when iterating throgh the map) to set/get struct values?
also can I somehow assure that MessageToDBEnum is exhaustive (all public keys of Message are included)?
You essentially want to convert a struct to map and vice versa. There are 3rd party libs to do that, e.g. github.com/mitchellh/mapstructure.
But we can do it ourselves too, it's not that hard. We may use reflection to do it. Without checking errors, here's the essence of the struct to map conversion:
func struct2Map(x interface{}) map[string]interface{} {
m := map[string]interface{}{}
v := reflect.ValueOf(x)
t := reflect.TypeOf(x)
for i := 0; i < v.NumField(); i++ {
m[t.Field(i).Name] = v.Field(i).Interface()
}
return m
}
You may use it like this:
msg := Message{
SomeKey: "v1",
SomeOtherKey: "v2",
}
m := struct2Map(msg)
fmt.Println(m)
Which outputs:
map[SomeKey:v1 SomeOtherKey:v2]
The backward conversion is even simpler, but know that for a function to modify a struct, you must pass a pointer to it. Again, without checking for possible errors, the essence of it is:
func map2Struct(m map[string]interface{}, d interface{}) {
s := reflect.ValueOf(d).Elem()
for k, v := range m {
s.FieldByName(k).Set(reflect.ValueOf(v))
}
}
Using it:
var msg2 Message
map2Struct(m, &msg2)
fmt.Printf("%+v\n", msg2)
Which outputs:
{SomeKey:v1 SomeOtherKey:v2}
Try the examples on the Go Playground.
You may build the validation into these conversion functions, and return an error or use default / zero values when an invalid value is found.

Why is a map value in one function affected by an entry to the map in another function?

Here's my code:
func test(v map[string]string) {
v["foo"] = "bar"
}
func main() {
v := make(map[string]string)
test(v)
fmt.Printf("%v\n", v) // prints map[foo:bar]
}
I'm pretty new to Go, but as far as I was aware, since I'm passing the map value to test() and not a pointer to the map, the test() function should modify a different variable of the map, and thus, not affect the value of the variable in main(). I would have expected it to print map[]. I tested a different scenario:
type myStruct struct {
foo int
}
func test2(v myStruct) {
v.foo = 5
}
func main() {
v := myStruct{1}
test2(v)
fmt.Printf("%v\n", v) // prints {1}
}
In this scenario, the code behaves as I would expect. The v variable in the main() function is not affected by the changes to the variable in test2(). So why is map different?
You are right in that when you pass something to a function, a copy will be made. But maps are some kind of descriptors to an underlying data structure. So when you pass a map value to a function, only the descriptor will be copied which will denote / point to the same data structures where the map data (entries) are stored.
This means if the function does any modification to the entries of the map (add, delete, modify entries), that is observed from the caller.
Read The Go Blog: Go maps in action for details.
Note that the same applies to slices and channels too; generally speaking the types that you can create using the built-in make() function. That's why the zero value of these types is nil, because a value of these types need some extra initialization which is done when calling make().
In your other example you are using a struct value, they are not descriptors. When you pass a struct value to another function, that creates a complete copy of the struct value (copying values of all its fields), which –when modified inside the function– will not have any effect on the original, as the memory of the copy will be modified – which is distinct.

go lang construct map based on filter criteria

I have below structures
type ModuleList []Module
type Module struct {
Id string
Items []Item
Env map[string]string
}
type Item struct {
Id string
Host string
}
I have a service which returns ModuleList; but I would like to create a function which can group ModuleList based on Module Env key value and return map[string]ModuleList or map[string]*Module
Can i have any sample function which does this ?
I had tried doing this
appsByGroup := make(map[string]ModuleList)
for _, app := range apps {
if _, ok := app.Env["APP_GROUP"]; ok {
appGroup := app.Env["APP_GROUP"]
appsByGroup[appGroup] = app
}
}
; but not quite sure how to add element to an array
If you want to group all the Modules by a APP_GROUP then you are pretty much correct. You are just not appending to the slice correctly:
appsByGroup := make(map[string]ModuleList)
for _, app := range apps {
if _, ok := app.Env["APP_GROUP"]; ok {
appGroup := app.Env["APP_GROUP"]
// app is of type Module while in appsGroup map, each string
// maps to a ModuleList, which is a slice of Modules.
// Hence your original line
// appsByGroup[appGroup] = app would not compile
appsByGroup[appGroup] = append(appsByGroup[appGroup], app)
}
}
Now you can access all the Modules (stored in a slice) in a group by using:
// returns slice of modules (as ModuleList) with
// Env["APP_GROUP"] == group
appGroups[group]

Obtaining reflect.Ptr type to field in a Go struct

I am trying to pass to a third-party package a variadic list of pointers to fields in a struct. The package accepts a variadic interface{} list ( func Persist(...interface) error ), where each of the interface values is a pointer to a variable. I created a function that mocks how the third-party library and prints out the Type and Kind of the pointers (called mockFunction below).
When I pass it the address of the struct variables in a non-variadic way, they have their primitive Types and Values within the mocked function using the reflect calls. However, when I pass them in a variadic way using expansion, they have Type: Type: reflect.Value and Kind: struct. The third-party package does not know how to handle them in this form.
I would like to figure out a way to call the third-party package with a slice of interface{} (e.g. inv := make([]interface{}, 3) and use variadic expansion on the call Persist(inv...) if at all possible.
Here is the code with a link to Go Playground below:
package main
import (
"fmt"
"reflect"
)
type Investment struct {
Price float64
Symbol string
Rating int64
}
func main() {
inv := Investment{Price: 534.432, Symbol: "GBG", Rating: 4}
s := reflect.ValueOf(&inv).Elem()
variableParms := make([]interface{}, s.NumField())
for i := 0; i < s.NumField(); i++ {
variableParms[i] = s.Field(i).Addr()
}
// non-variadic call
mockFunction(&inv.Price, &inv.Symbol, &inv.Rating)
//variadic call
mockFunction(variableParms...)
}
func mockFunction(values ...interface{}) {
for i, value := range values {
rv := reflect.ValueOf(value)
fmt.Printf("value %d has Type: %s and Kind %s\n", i, rv.Type(), rv.Kind())
}
}
Go Playground Link
When I run it with the non-variadic parameters, the call to mockFunction returns the native Types and Kinds and the third-party package processes them fine:
value 0 has Type: *float64 and Kind ptr
value 1 has Type: *string and Kind ptr
value 2 has Type: *int64 and Kind ptr
When I run it with the variadic parameters, the values are different and the third-party package does not know how to handle these types:
value 0 has Type: reflect.Value and Kind struct
value 1 has Type: reflect.Value and Kind struct
value 2 has Type: reflect.Value and Kind struct
Is there any way to structure the slice definition and the call to what is placed in to the slice so that it can be variadic expanded and look like passing the pointers to the struct fields in the non-variadic way?
Addr() returns the reflect Value for the field pointer. Call Ptr() on the value to get the actual pointer as an interface{}.
variableParms[i] = s.Field(i).Addr().Ptr()
playground
I think that perhaps Go's handling for this case has changed since 2014 - certainly the code above no longer works for me with Go 1.10...
However the following code works for me to create an appropriate []interface{} to use in the described way...
func settableSliceFromStruct(inStruct interface{}) ([]interface{}, error) {
t := reflect.TypeOf(inStruct)
if t.Kind() != reflect.Ptr {
return nil, errors.New("can only assign values with pointer to struct")
}
v := reflect.ValueOf(inStruct).Elem()
t = t.Elem()
dataColumns := make([]interface{}, 0, t.NumField())
for i := 0; i < t.NumField(); i++ {
if weWantToIncludeThis(t.Field(i)) {
dataColumns = append(dataColumns, v.Field(i).Addr().Interface())
}
}
return dataColumns, nil
}
The critical part here would be for your code to use:
variableParms[i] = s.Field(i).Addr().Interface()

Resources