How to update value of two struct efficiently - loops

I have the following code which parses YAML files and needs to match values from one struct external and update the internal struct's type property.
For example, this is the yaml file(translated to bin for simplicity) and content which is parsed correctly
package main
import (
"fmt"
"gopkg.in/yaml.v2"
"log"
)
//internal config model for parsing
type InternalModel struct {
models []Model2 `yaml:"models"`
}
type Model2 struct {
Name string `yaml:"name"`
Type string `yaml:"type"`
Target string `yaml:"target"`
}
var internal_config = []byte(`
models:
- name: myapp
type: app1
target: ./
- name: myapp2
type: app2
target: ./
`)
type ExternalConfig struct {
Landscape Zone `yaml:"Landscape"`
}
type Zone struct {
Zone string `yaml:"zone"`
Models []Model `yaml:"models"`
}
type Model struct {
AppType string `yaml:"app-type"`
ServiceType string `yaml:"service-type"`
}
var external_config = []byte(`
Landscape:
zone: zone1
models:
- app-type: app1
service-type: GCP
- app-type: app2
service-type: AMAZON
zone: zone2
models:
- app-type: app3
service-type: AZURE
- app-type: app4Í
service-type: HEROKU
`)
func main() {
// This is the internal config which needs updated
internalConfiglYaml := InternalModel{}
err := yaml.Unmarshal([]byte(internal_config), &internalConfiglYaml)
if err != nil {
log.Fatalf("error in model internalConfiglYaml: %v", err)
}
//fmt.Printf("%+v\n", internalConfiglYaml)
//--------------------------Second config file-----------------------//
//This is the external config yaml
extConfigYaml := ExternalConfig{}
err = yaml.Unmarshal([]byte(external_config), &extConfigYaml)
if err != nil {
log.Fatalf("error in model extConfigYaml: %v", err)
}
fmt.Printf("%+v\n", extConfigYaml)
landscape := "zone1"
modifiedConfig := ConvertTypes(internalConfiglYaml, extConfigYaml, landscape)
fmt.Printf("%+v\n", modifiedConfig)
}
func ConvertTypes(int_cfg InternalModel, ext_config ExternalConfig, landscape string) (out_cfg InternalModel) {
for _, module := range int_cfg.models {
switch module.Type {
case "app1":
//here I hard-coded the value "GCP" but it should come from the yaml struct after parsing
module.Type = "GCP" // should be something like ext_config.models.service-type when the key in the struct
case "app2":
//here I hard-coded the value "AMAZON" but it should come from the yaml struct after parsing
module.Type = "AMAZON"
}
}
return int_cfg
}
//At the end what I need to do is to get the internal yaml file to be changed to the following struct
//The changes are when the type=app-type I need to modify the type in the internal config, here its GCP and ruby
//internal_config_after_changes := []byte(`
//
//
//models:
// - name: myapp
// type: GCP
// target: ./
//
// - name: myapp2
// type: AMAZON
// target: ./
//
//
//`)
At the end what I need to do is to get the internal yaml file to be changed to the struct above internal_config_after_changes
The changes are when the type=app-type I need to modify the type value in the internal_config, here from app1 to GCP and app2 to amazon
The problem is with the second loop which I should use to iterate on the external_config and the matching values, I'm not sure how to combine them both with efficient way...

Golang FAQ described regarding pointers to maps and slice:
Map and slice values behave like pointers: they are descriptors that
contain pointers to the underlying map or slice data. Copying a map or
slice value doesn't copy the data it points to. Copying an interface
value makes a copy of the thing stored in the interface value. If the
interface value holds a struct, copying the interface value makes a
copy of the struct. If the interface value holds a pointer, copying
the interface value makes a copy of the pointer, but again not the
data it points to.
On iterating through the slice of model inside ConvertType you are actually creating a copy of []Models slice whose value.Type is not changing the value of original struct due that reason.
for _, module := range int_cfg.models{}
Above code snippet is creating a copy of int_cfg.models{}.
Index the slice model to point to the exact underlying array of slice Model to change the value as:
package main
import (
"fmt"
"log"
"strings"
"gopkg.in/yaml.v2"
)
//internal config model for parsing
type InternalModel struct {
Models []Model2 `yaml:"models"`
}
type Model2 struct {
Name string `yaml:"name"`
Type string `yaml:"type"`
Target string `yaml:"target"`
}
var internal_config = []byte(`
models:
- name: myapp
type: app1
target: ./
- name: myapp2
type: app2
target: ./
`)
type ExternalConfig struct {
Landscape []Zone `yaml:"Landscape"`
}
type Zone struct {
Zone string `yaml:"zone"`
Models []Model `yaml:"models"`
}
type Model struct {
AppType string `yaml:"app-type"`
ServiceType string `yaml:"service-type"`
}
var external_config = []byte(`
Landscape:
- zone: zone1
models:
- app-type: app1
service-type: GCP
- app-type: app2
service-type: AMAZON
- zone: zone2
models:
- app-type: app3
service-type: AZURE
- app-type: app4Í
service-type: HEROKU
`)
func main() {
//This is the internal config which needs to be update
internalConfiglYaml := InternalModel{}
err := yaml.Unmarshal(internal_config, &internalConfiglYaml)
if err != nil {
log.Fatalf("error in model internalConfiglYaml: %v", err)
}
fmt.Printf("%+v\n", internalConfiglYaml)
//--------------------------Second config file-----------------------//
//This is the external config yaml
extConfigYaml := ExternalConfig{}
// var response interface{}
err = yaml.Unmarshal(external_config, &extConfigYaml)
if err != nil {
log.Fatalf("error in model extConfigYaml: %v", err)
}
fmt.Printf("%+v\n", extConfigYaml)
landscape := "zone1"
modifiedConfig := ConvertTypes(&internalConfiglYaml, extConfigYaml, landscape)
fmt.Printf("%+v\n", modifiedConfig)
}
// ConvertTypes for changing the intConfig struct types
func ConvertTypes(int_cfg *InternalModel, ext_config ExternalConfig, landscape string) (out_cfg *InternalModel) {
for _, module := range ext_config.Landscape {
if module.Zone == landscape {
for i, value := range module.Models {
switch strings.Compare(value.AppType, int_cfg.Models[i].Type) {
case 0:
//here I hard-coded the value "GCP" but it should come from the yaml struct after parsing
int_cfg.Models[i].Type = value.ServiceType // should be something like ext_config.models.service-type when the key in the struct
default:
}
}
}
}
return int_cfg
}
If you check above code snippet, you will also recognize that I have changed the struct.
type InternalModel struct {
models []Model2 `yaml:"models"`
}
to first letter uppercase to make it exportable as:
type InternalModel struct {
Models []Model2 `yaml:"models"`
}
Because of the struct InternalModel is unexportable field model was unable to parse the provided internal_config yaml which leads to empty []slice data after unmarshal yaml.
One more thing that I have noticed is you are converting bytes into bytes again. There is no need for that.
err := yaml.Unmarshal([]byte(internal_config), &internalConfiglYaml)
So I have changed it to just:
err := yaml.Unmarshal(internal_config, &internalConfiglYaml)
Since internal_config is already declared as byte using []byte in global variable.

If you're looking for how to apply the app-type to service-type mapping from the ExternalConfig, I suggest converting the array into a map and using it to look up the mapping:
func ConvertTypes(intCfg *InternalModel, extCfg ExternalConfig, landscape string) {
// Make an app-type to service-type map.
appToSvc := make(map[string]string, len(extCfg.Landscape.Models))
for _, m := range extCfg.Landscape.Models {
appToSvc[m.AppType] = m.ServiceType
}
// For each InternalModel model, check if there's an app-type to service-type
// mapping. If so, use the service-type instead of the app-type.
for i, model := range intCfg.Models {
if t, ok := appToSvc[model.Name]; ok {
intCfg.Models[i].Type = t
}
}
}
I've also fixed the pass-by-pointer and range loop assignment problems as suggested by #Himanshu and #Zan Lynx.

The first thing to consider is that you are working with two structures that are defined at run-time, thus you will not be able to use a single loop to match them. The next thing to consider is that your InternalConfig is the one least likely to change and should thus be the first structure to parse, where after the ExternalConfig is parsed and matched with it.
From reading your question, comments and the provided answers it also seems that there are a number of assumptions on the board which few have alluded to. I will attempt to address these if possible.
So let's give it a go:
package main
import (
// import standard library packages first (alphabetically)
"fmt"
"log"
// import third party packages next (also alphabetically)
"gopkg.in/yaml.v2"
// finally import your own packages here
)
// InternalConfig
// - Let's call it InternalConfig so as to ensure consistent naming standards
type InternalConfig struct {
Models []Imodel `yaml:"models"`
// Exported to ensure access to external packages (such as yaml)
}
// Imodel - The internal model struct
type Imodel struct {
Name string `yaml:"name"`
Type string `yaml:"type"`
Target string `yaml:"target"`
}
var iCfgb = []byte(`
models:
- name: myapp
type: app1
target: ./
- name: myapp2
type: app2
target: ./
`)
type ExternalConfig struct {
Landscape Zone `yaml:"Landscape"`
}
type Zone struct {
Zone string `yaml:"zone"`
Models []Emodel `yaml:"models"`
}
type Emodel struct {
AppType string `yaml:"app-type"`
ServiceType string `yaml:"service-type"`
}
var eCfgb = []byte(`
Landscape:
zone: zone1
models:
- app-type: app1
service-type: GCP
- app-type: app2
service-type: AMAZON
zone: zone2
models:
- app-type: app3
service-type: AZURE
- app-type: app4Í
service-type: HEROKU
`)
function main() {
iCfgYaml := InternalConfig{}
// don't need []byte(iCfgb) as iCfgb is already a byte slice
// We are also using a shorthand if statement as we will only use err in the
// if scope, this is because our config is passed by reference and not value
if err := yaml.Unmarshal(iCfgb, &iCfgYaml); err != nil {
log.Fatalf("error in model InternalConfig: %v", err)
}
eCfgYaml := ExternalConfig{}
if err := yaml.Unmarshal(eCfgb, &eCfgYaml); err != nil {
log.Fatalf("error in model ExternalConfig: %v", err)
}
// From here there are clearly some assumptions made, here is my take:
// 1. I do not see any purpose in the landscape variable, the value "zone1"
// can be obtained from eCfgYaml.Landscape.Zone;
// 2. From the question it seems you want to update the InternalConfig
// values with values from the ExternalConfig. However, from the code you
// created a modified copy. I will assume this was for debugging purposes
// and skip the creating of another copy;
// I have renamed ConvertTypes to UpdateInternalState to accurately reflect
// the behavior of the function. It now returns an error instead of a copy of
// InternalConfig (any state altering functions should most likely return an
// error). It receives a pointer of InternalConfig and a value copy of
// ExternalConfig. This is because we will be altering the state of
// InternalConfig
if err := UpdateInternalState(&iCfgYaml, eCfgConfig); err != nil {
log.Fatalf("Error updating internal config: %v", err)
}
// Assume the print is for debugging, thus I denote it with DBG. Makes it easy
// to find and remove from production code.
fmt.Printf("DBG: %+v\n", iCfgYaml)
}
func UpdateInternalState(iCfg *InternalConfig, eCfg ExternalConfig) error {
// Firstly, we need to track the internal config models. We will create an
// index lookup map (ilm) for it.
ilm := make(map[string]int)
// Now we iterate through our internal models and update the index:
for i, v := range iCfg.Models {
ilm[v] = i
}
// Now we iterate through the external config and update the internal config
// state accordingly:
for _, em := range eCfg.Landscape.Models {
// We find the index of the specified app type in the index lookup
// We use a shorthand if statement as we will only use i and ok within
// the scope of the if (if you need it outside you can't use shorthand)
// If there is no match nothing happens, but if found it will update to
// the corresponding specified value in the external config.
if i, ok := ilm[em.AppType]; ok {
iCfg.Models[i].Type = em.ServiceType
}
}
// At any point within this function you are able to do more robust error
// checking, such as ensuring the index lookup map must have a corresponding
// entry, and return any errors encountered.
return nil
}

This question is definitely a duplicate but I can't find one to link right now.
Your problem is that the for loop is creating a copy of each struct. Writing into element which is just a copy of xtr[i] never changes the original xtr[i]. What you do to fix it is to use the slice index instead.
Change
for _, element := range xtr {
switch element.vType {
case "app1":
//user1 value is coming from the &y parsed data of the yaml
element.vType = "user1"
case "app2":
//user2 value is coming from the &y parsed data
element.vType = "user2"
}
}
Into
for i := range xtr {
switch xtr[i].vType {
case "app1":
//user1 value is coming from the &y parsed data of the yaml
xtr[i].vType = "user1"
case "app2":
//user2 value is coming from the &y parsed data
xtr[i].vType = "user2"
}
}

Related

Calling valiable name from another variable

Please help to call the variable from another variable.
I have the script which is taking EC2 instances and return in "*ec2.Instance" variable.
I can print information from static text, for example :
fmt.Println(instance.InstanceType) // t3.small
But I have the list of reqired fields like and don't know how dynamic use name from this list :
fields := []string{"InstanceId", "InstanceType", "PrivateIpAddress"}
for i := range fields {
fmt.Println(fields[i])
fmt.Println(instance.fields[i]) // Not correct ... :(
}
You need to use reflection to do this in go.
The key takeaway is you need to "analyze" the returned value at runtime and access properties by name from the "reflected" structure. Reflection basically means analyzing objects at runtime.
package main
import (
"fmt"
"reflect"
)
type whatever struct {
cat string
dog string
animals int
something string
}
func main() {
wantProps := []string{ "cat", "animals"}
we := whatever{cat: "meow", animals: 22}
r := reflect.ValueOf(we)
for _, propName := range wantProps {
prop := r.FieldByName(propName)
fmt.Println(propName, prop)
}
}
More details:
Golang dynamic access to a struct property

type interface {} does not support indexing in golang

I have such map:
Map := make(map[string]interface{})
This map is supposed to contain mapping from string to array of objects. Arrays can be of different types, like []Users or []Hosts. I populated this array:
TopologyMap["Users"] = Users_Array
TopologyMap["Hosts"] = Hosts_Array
but when I try to get an elements from it:
Map["Users"][0]
it gives an error:
(type interface {} does not support indexing)
How can I overcome it?
You have to explicitly convert your interface{} to a slice of the expected type to achieve it. Something like this:
package main
import "fmt"
type Host struct {
Name string
}
func main() {
Map := make(map[string]interface{})
Map["hosts"] = []Host{Host{"test.com"}, Host{"test2.com"}}
hm := Map["hosts"].([]Host)
fmt.Println(hm[0])
}
Playground link
First thing to be noted is the interface{} can hold any data type including function and struct or []struct. Since the error gives you :
(type interface {} does not support indexing)
It means that it holds no slice or no array values. Because you directly call the index in this case is 0 to an interface{} and you assume that the Map["Users"] is an array. But it is not. This is one of very good thing about Go it is statically type which mean all the data type is check at compiled time.
if you want to be avoid the parsing error like this:
panic: interface conversion: interface {} is []main.User, not
[]main.Host
To avoid that error while your parsing it to another type like Map["user"].([]User) just in case that another data type pass to the interface{} consider the code snippet below :
u, ok := myMap["user"].([]User)
if ok {
log.Printf("value = %+v\n", u)
}
Above code is simple and you can use it to check if the interface match to the type you are parsing.
And if you want to be more general passing the value to your interface{} at runtime you can check it first using reflect.TypeOf() please consider this code :
switch reflect.TypeOf(myMap["user"]).String() {
case "[]main.User":
log.Println("map = ", "slice of user")
logger.Debug("map = ", myMap["user"].([]User)[0])
case "[]main.Host":
log.Println("map = ", "slice of host")
logger.Debug("map = ", myMap["user"].([]Host)[0])
}
after you know what's the value of the interface{} you can confidently parse it the your specific data type in this case slice of user []User. Not that the main there is a package name you can change it to yours.
This is how I solved it for unstructured data. You have to parse to index string until you reach the end. Then you can print key value pairs.
yamlStr := `
input:
bind: 0.0.0.0:12002
interface: eth0
reaggregate: {}
versions: {}
output:
destination: owl-opsw-sw-dev-4.opsw:32001
interface: eth0
`
var obj map[string]interface{}
if err := yaml.Unmarshal([]byte(yamlStr), &obj); err != nil {
// Act on error
}
// Set nested object to map[string]
inputkv := streamObj["input"].(map[string]interface{})
for key, value := range inputkv {
// Each value is an interface{} type, that is type asserted as a string
fmt.Println(key, value.(string))
}
Result
bind 0.0.0.0:12002
interface eth0

save and loading custom types

I have a custom type map[string]map[string]string am trying to save in Google Datastore, the save works as expected. However the load functions complains about assignment to entry in nil map
//Address represents address
type Address map[string]map[string]string
Type above is a map of map[string]string, target at saving different address types.
//Load function from PropertyLoaderInterface helps datastore load this object
func (a *Address) Load(dp []datastore.Property) error {
for _, property := range dp {
(*a)[property.Name] = util.InterfaceToMapString(property.Value)
}
return nil
}
In the load function I deference the Address which is a map of map[string]string, it saves the following example JSON format.
"Company":{
"physicalAddress": "",
"postalAddress": "",
"physicalCity": "",
"postalCity": "",
"physicalCode": "",
"postalCode": "",
"physicalCountry": "",
"postalCountry": ""
}
Save function below works well and data is store in datastore. The Load is however rather a tricky bugger.
//Save function from PropertyLoaderInterface helps datastore save this object
func (a *Address) Save() ([]datastore.Property, error) {
propertise := []datastore.Property{}
for name, value := range *a {
propertise = append(propertise, datastore.Property{Name: name,
NoIndex: true,
Value: util.MapToJSONString(value)})
}
return propertise, nil
}
Working load for Address struct
func (a *Address) Load(dp []datastore.Property) error {
*a = make(Address)
for _, property := range dp {
(*a)[property.Name] = util.InterfaceToMapString(property.Value)
}
return nil
}
First, regarding the declarations - https://stackoverflow.com/a/42901715/4720042
Next, I feel you should instead use a custom struct for this purpose.
Even if you still want to use a map[string]map[string]string, you cannot assign to a field in a map that hasn't been explicitly defined viz. property.Name
You have to initialize that map with make if you plan on adding the elements later.

Loading page faster by storing keys into sessions golang

I am trying to load dynamic page faster. I am making twitter clone as a learning assignment. I am following below approach
When somebody tweets, store the tweet in datastore and safe the same in memcache { key.string(), json.Marshal(tweet) }
I push the tweet in User home time line. The home time line is a []*datastore.Key, which is stored in user session (which get copied in memcache and then in DB).
When user open her homepage, the homepage try to get the Keys from session, if not found then it make a datastore query.
Once i get the keys I fetch the tweets from memcache (if not then from db)
I am stuck at step 3.
In first case I am getting the correct information but in string slices (not in []*datastore.Key).
In second case I getting this error
2016/09/03 17:23:42 http: panic serving 127.0.0.1:47104: interface
conversion: interface is []interface {}, not []datastore.Key
Kindly help me where I am going wrong and is there a better way.
case 1
func GetKeys(req *http.Request, vars ...string) []interface{} {
//GetKeys - get the keys
s, _ := GetGSession(req)
var flashes []interface{}
key := internalKey
if len(vars) > 0 {
key = vars[0]
}
if v, ok := s.Values[key]; ok {
// Drop the flashes and return it.
// delete(s.Values, key)
flashes = v.([]interface{})
}
return flashes
}
Case2
//GetHTLKeys - get the hometimeline keys
func GetHTLKeys(req *http.Request, vars ...string) []datastore.Key {
s, _ := GetGSession(req)
var keyList []datastore.Key
key := internalKey
if len(vars) > 0 {
key = vars[0]
}
if v, ok := s.Values[key]; ok {
keyList = v.([]datastore.Key)
}
return keyList
}
Your issue is that you can't assert an []interface{} is a []datastore.Key. This is because they are different.
What you can do at this point, is type assert v to a []interface{} then loop through the slice and type assert each element.
Here is an example demonstrating this (playground version):
type I interface {
I()
}
type S struct{}
func (s S) I() {
fmt.Println("S implements the I interface")
}
// Represents you getting something out of the session
func f(s S) interface{} {
return []interface{}{s}
}
func main() {
v := f(S{})
//v2 := v.([]I) would panic like in your example.
v2, ok := v.([]interface{})
if !ok {
// handle having a non []interface{} as a value
}
for _, v3 := range v2 {
v4, ok := v3.(I)
if !ok {
// handle having a non-I in the slice
}
v4.I() //you definitely have an I here
}
}

Slice and interface manipulation

I have recently started programming with Go on Google App Engine and I have run into a road block. I come from Java land so it's been a slight struggle to adapt to Go.
I want to have a method that allows me to pass in a pointer to a slice that I can then pass into the datastore.GetAll call to retrieve the results. I then want to iterate through the results and use an assertion to cast as a specific interface (Queryable) in order to call a method Map().
Initially, I had this functioning properly:
func (s ProjectService) RunQuery(context context.Context, q *datastore.Query, projects *[]Project) error {
keys, err := q.GetAll(context, projects)
if err != nil {
return err
}
for i, key := range keys {
(*projects)[i].Id = key.Encode()
(*projects)[i].CompanyId = (*projects)[i].Company.Encode()
}
return nil
}
I want to have a more generic method that can be applied to any entity that implements a Queryable interface. The idea is to have a hook that allows me to perform some post processing after retrieving the results. I've looked into the ProperyLoadSaver interface however I have no access to the actual key that is associated to the entity. I would like to store the string representation of the datastore.Key in the entity.
This is the Queryable interface:
type Queryable interface {
Map(*datastore.Key) error
}
Here's an example entity that I am persisting to the GAE store:
type Camera struct {
Id string `datastore:"-"`
ProjectId string `datastore:"-"`
Name string
Project *datastore.Key `json:"-"`
Active bool
Timestamp Timestamp
}
// Implement Queryable interface. Let me perform any additional mapping
func (c *Camera) Map(key *datastore.Key) error {
c.Name = "Maybe do other things here"
c.Id = key.Encode()
return nil
}
The idea is to have something like the snippet below.
func (c Crud) RunQuery(context context.Context, q *datastore.Query, entities interface{}) error {
keys, err := q.GetAll(context, entities)
v := reflect.ValueOf(entities)
dv := v.Elem()
for i, key := range keys {
// I left this in to show that this worked however this won't let me enforce the interface contract
//dv.Index(i).FieldByName("Id").Set(reflect.ValueOf(key.Encode()))
entity := dv.Index(i).Interface().(Queryable)
entity.Map(key)
}
return err
}
However, when this executes, it panics with the following:
PANIC: interface conversion: entity.Camera is not entity.Queryable: missing method Map goroutine 9 [running]:
Just as a note, I realize the appropriate way to perform an assertion is to do if as, ok := elem.(Type); ok {} but I just wanted to see what the error was
I am guessing I am getting this error because I have defined my parameter with a pointer receiver func (c *Camera) Map(key *datastore.Key) error and not func (c Camera) Map(key *datastore.Key) error However, I want to modify the actual value.
Where am I going wrong with this? Is my Java-ness showing?
Being that I am very new to Go, I may be approaching this completely wrong.
Because the method is on a pointer receiver (as it should be), use the address of the slice element:
entity := dv.Index(i).Addr().Interface().(Queryable)
An alternative approach is to use a slice of pointers for the result:
var result []*Camera
err := c.RunQuery(ctx, q, &result)
The code can be written to work with both []Camera or []*Camera as follows:
var queryableType = reflect.TypeOf((*Queryable)(nil)).Elem()
needAddr := !dv.Type().Implements(queryableType)
...
var entity Queryable
if needAddr {
entity = dv.Index(i).Addr().Interface().(Queryable)
} else {
entity = dv.Index(i).Interface().(Queryable)
}

Resources