I've got a struct like below:
type Page struct {
title string
url string
}
and a map of structs:
var mostViewed = make(map[int]Page)
With go-cache, I store the map with a TTL time.
c.Set("data", mostViewed, 60*time.Minute)
But, once I recover "data" key, how could I assing it back to a map?
a, _ := c.Get("data")
fmt.Printf("%+v\n", a)
out: map[17:{title:xxx, url:yyy}]
I tried something like:
z := map[int]Page{a}
Any clue? It's like "remapping" a mapped string.
You get an interface{} type back, but you know what it is, so you need to use a type assertion to cast it back to a map[int]Page. Here is a quick external resource. https://newfivefour.com/golang-interface-type-assertions-switch.html
Here is an example
https://play.golang.org/p/lGseg88K1m
type Page struct {
title string
url string
}
func main() {
m := make(map[int]Page)
m[1] = Page{"hi", "there"}
iface := makeIface(m)
// use type assertion to cast it back to a map
if mapAgain, ok := iface.(map[int]Page); ok {
// it really is a map[int]Page
fmt.Printf("%+v\n", mapAgain)
} else {
// its not actually a map[int]Page
fmt.Println("oops")
}
// alternatively use a type-switch if it could be multiple types
switch v := iface.(type) {
case map[int]Page:
//yay
fmt.Printf("%+v\n", v)
case string:
// its a string
fmt.Println(v)
default:
// something we didn't account for
}
}
func makeIface(m map[int]Page) interface{} {
return m
}
Edit: as a side note, you probably want to make your map type map[int]*Page because if you did something like this:
page := m[1]
page.url = "different"
fmt.Println(page) // prints url="different"
fmt.Println(m[1].url) // remains unchanged
Because page would be a copy of what is in the map, not the Page in the map itself.
Related
I am new to GOlang, I trying to filter slice of struct in GO using struct which contain some parameter for filter it. I am trying to do below things but its not work for me. In that code I have filter function which take slice of struct i.e GRN which need to filter using FilterParameter struct.
My Struct which need to filer
type GRN struct {
DocType string `json:"docType"`
GRNNO string `json:"GRNNO"`
PRID string `json:"PRID"`
PRODUCE string `json:"PRODUCE"`
VARIETY string `json:"VARIETY"`
}
manyGRNUsers := []GRN{{"GRNAsset","GRN0000109","PRFAINAPKR0086","Sweetcorn","CP2"},
{"GRNAsset","GRN0000110","PRFAINAPKR0087","Sweetcorn","CP1",},
{"GRNAsset","GRN0000112","PRFAINAPKR0087", "Sweetcorn","CP2",},
{"GRNAsset","GRN0000113","PRFAINAPKR0089","Apple","Gala",}}
Struct which will used for filter parameter.here one thing the field in FilterParameter is dyanamic as above GRN struct value. it could me more then two field but less than or equal to GRN struct field
type FilterParameter struct {
PRODUCE string `json:"PRODUCE"`
VARIETY string `json:"VARIETY"`
}
manyFilterParameter := []FilterParameter{{"Sweetcorn","CP2" }
I tried below function but its not work for me as I can do only with one field from FilterParameter i.e PRODUCE only and its not dynamic
func filter(fu []GRN, su []FilterParameter) (out []GRN) {
f := make(map[string]struct{}, len(su))
for _, u := range su {
f[u.PRODUCE] = struct{}{}
}
for _, u := range fu {
if _, ok := f[u.PRODUCE]; ok {
out = append(out, u)
}
}
return
}
You can't really do what you're trying to do, as it's not possible to iterate the fields of a struct (maybe with reflection). For another approach, you could use maps instead of structs, or even easier, if you can just explicitly use the fields you care about in the function parameters:
package main
import "fmt"
type GRN struct { DocType, GRNNO, PRID, PRODUCE, VARIETY string }
func filter(in []GRN, produce, variety string) []GRN {
var out []GRN
for _, each := range in {
if each.PRODUCE == produce && each.VARIETY == variety {
out = append(out, each)
}
}
return out
}
func main() {
manyGRNUsers := []GRN{
{"GRNAsset","GRN0000109","PRFAINAPKR0086","Sweetcorn","CP2"},
{"GRNAsset","GRN0000110","PRFAINAPKR0087","Sweetcorn","CP1"},
{"GRNAsset","GRN0000112","PRFAINAPKR0087", "Sweetcorn","CP2"},
{"GRNAsset","GRN0000113","PRFAINAPKR0089","Apple","Gala"},
}
out := filter(manyGRNUsers, "Sweetcorn", "CP2")
fmt.Println(out)
}
I want to translate some fields of my Rest response from English to Hindi language. I have few translator files where I have mapping of words from English to Hindi. The name of the file I want to provide via field tags.
So my struct will look something like this
type myResponse struct {
City string `translatorFile:"CityEToH"`
State string `translatorFile:"StateEToH"`
StationCode []string `translatorFile:"StationCodeEToH"`
InsideStruct insideStruct
}
type insideStruct struct {
trainName string `translatorFile:"TrainEToH"`
StartingPoint string `translatorFile:"StationCodeEToH"`
FinishPoint string `translatorFile:"StationCodeEToH"`
}
I want to write a common translator method that will take interface{} as the input parameter and will return an interface (after converting the input) as output. I have just started to learn Go and I am stuck with the implementation. I am not able to create a map kind of structure that will map the fieldName to the corresponding translator file Name.
I have tried reflect.typeOf(input), but with this I am not able to get the tags of insideStruct. This is just an example structure of payload, I could have 4-5 inherited level of struct as well.
Is there a way to get the fieldName, tags and fieldValue together. Or is there any other better way to implement this ?
Here's a function that walks through values and calls a function for each string with the associated struct tag:
func walkStrings(v reflect.Value, tag reflect.StructTag, fn func(reflect.Value, reflect.StructTag)) {
v = reflect.Indirect(v)
switch v.Kind() {
case reflect.Struct:
t := v.Type()
for i := 0; i < t.NumField(); i++ {
walkStrings(v.Field(i), t.Field(i).Tag, fn)
}
case reflect.Slice, reflect.Array:
if v.Type().Elem().Kind() == reflect.String {
for i := 0; i < v.Len(); i++ {
walkStrings(v.Index(i), tag, fn)
}
}
case reflect.String:
fn(v, tag)
}
}
The function fn can use Value.String to get the value as a string and Value.SetString to change the value. Use StructTag.Get to get the tag. An example function is:
func translate(v reflect.Value, tag reflect.StructTag) {
if !v.CanSet() {
// unexported fields cannot be set
return
}
file := tag.Get("translatorFile")
if file == "" {
return
}
v.SetString(translatStringWithFile(v.String(), file)
}
Call walkStrings with a reflect.Value of a struct pointer and the empty string as the tag:
v := myResponse{
City: "mycity",
StationCode: []string{"code1", "code2"},
InsideStruct: insideStruct{"trainname", "start", "finish"},
}
walkStrings(reflect.ValueOf(&v), "", translate)
Run it on the playground.
I'm having a bit of trouble figuring out how to either create a struct in a switch statement or assign a type to it in a switch statement.
Here's some non-working code illustrating what I'm trying to do:
var result
switch structPickingString {
case "struct1":
result = new(struct1)
case "struct2":
result = new(struct2)
}
//unmarshall some json into the appropriate struct type
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
log.Println(err)
}
//print out that json with a function specific to that type of struct
result.Print()
I think something involving an empty interface{} might be related to solving this issue but unfortunately I'm still a bit ignorant with golang and I'm not seeing how to make it work.
Here's a link to a slightly modified version of the code for more context: https://play.golang.org/p/Rb1oaMuvmU2
The issue is not defining the print function, it's assigning a specific type of struct to the result variable based on using the individual Print function that the struct implements.
Let me know if there's any more info I could provide.
Since you are calling .Print, you need to use an interface which has that method. I think you are looking for something like
type Printer interface {
Print()
}
func (s *struct1) Print() {
// ...
}
func (s *struct2) Print() {
// ...
}
var result Printer
switch structPickingString {
case "struct1":
result = new(struct1)
case "struct2":
result = new(struct2)
}
https://play.golang.org/p/W9r6UfeQSCz
You needs match "generical interface" with structs. See this:
//my struct1
type MyStruct1 struct {
ID int
}
//my struct2
type MyStruct2 struct {
ID int
}
//my interface
type MyStructGenerical interface {
Print()
}
//method (match with MyStructGenerical)
func (m1 MyStruct1) Print() {
println(m1.ID)
}
//method (match with MyStructGenerical)
func (m2 MyStruct2) Print() {
println(m2.ID)
}
In this way you can does assertions between structs and generical interface. See this:
//here result is an generical interface
var result MyStructGenerical = nil
//checkin
switch structPickingString {
case "struct1":
tmp := new(MyStruct1)
result = tmp
case "struct2":
tmp := new(MyStruct2)
result = tmp
}
The result is:
//unmarshall some json into the appropriate struct type
if err := json.NewDecoder(body()).Decode(&result); err != nil {
log.Println(err)
}
//print out that json with a function specific to that type of struct
tmp := result.(*MyStruct1)
tmp.Print()
// or
result.Print()
Run in: https://play.golang.org/p/nHrJnImsqNN
I think you want to use an interface like
type ResultIface interface {
Print()
}
Then you can do
var result ResultIface
switch structPickingString {
case "struct1":
result = new(struct1)
case "struct2":
result = new(struct2)
}
As long as your struct1 and struct2 fulfills the ResultIface by having a Print function it will print
I suspect I am trying to shoehorn go into behaving in an OOP way, but I don't know the go idiom to do what I want.
I have a Message struct that I use to pass data around in a client-server application:
type Message struct {
ID string `json:"id,omitempty"`
Type string `json:"type"`
Data interface{} `json:"data"`
}
the Data here can be different things, for example a number of Commands:
type Command struct {
User *types.UserInfo `json:"user"`
}
type CommandA struct {
Command
A *AData `json:"a_data"`
}
type CommandB struct {
CommandB
B *BData `json:"b_data"`
}
What I want to do is to check that the message data type is a Command and perform actions that are common to all commands, for example authorisation, all in one place and not having to type assert what type of command, calling the appropriate handler function and then do the auth, as this would result in massive code duplication.
The code below reflects what I would like to happen.
for {
select {
case m := <-in:
// what I would like to do, obviously not working as
// m.Data is not of type Command but the actual command type
if c, ok := m.Data.(msg.Command); ok {
// do auth and other common stuff
}
switch t := m.Data.(type) {
case *msg.CommandA:
go srv.handleCommandA(m.ID, t)
case *msg.CommandB:
go srv.handleCommandB(m.ID, t)
// etc etc
default:
// do something
}
}
}
How do I solve this go idiomatically?
You can define common command stuff in interface
type Commander interface{
DoCommonStuff()
}
implement it for Command struct
func (c Command) DoCommonStuff(){
//do stuff
}
and then assert
if c, ok := m.Data.(Commander); ok {
c.DoCommonStuff()
}
your other code should work unchanged
One approach is using reflection to extract common field value from the Data. In your example, since all Command has User field, we can use it to identify whether Message.Data is a command or not. If Command is not embedded to the data, simply return nil. Example code:
func GetUserInfo(v interface{}) *types.UserInfo {
vt := reflect.ValueOf(v)
if vt.Kind() == reflect.Ptr {
vt = vt.Elem()
}
if vt.Kind() != reflect.Struct {
return nil
}
u := vt.FieldByName("User")
if !u.IsValid() {
return nil
}
user, ok := u.Interface().(*types.UserInfo)
if !ok {
return nil
}
return user
}
//Call GetUserInfo then perform common operation
user := GetUserInfo(m.Data)
if user != nil {
//Do auth and other common stuff
}
In Go, http form data (e.g. from a POST or PUT request) can be accessed as a map of the form map[string][]string. I'm having a hard time converting this to structs in a generalizable way.
For example, I want to load a map like:
m := map[string][]string {
"Age": []string{"20"},
"Name": []string{"John Smith"},
}
Into a model like:
type Person struct {
Age int
Name string
}
So I'm trying to write a function with the signature LoadModel(obj interface{}, m map[string][]string) []error that will load the form data into an interface{} that I can type cast back to a Person. Using reflection so that I can use it on any struct type with any fields, not just a Person, and so that I can convert the string from the http data to an int, boolean, etc as necessary.
Using the answer to this question in golang, using reflect, how do you set the value of a struct field? I can set the value of a person using reflect, e.g.:
p := Person{25, "John"}
reflect.ValueOf(&p).Elem().Field(1).SetString("Dave")
But then I'd have to copy the load function for every type of struct I have. When I try it for an interface{} it doesn't work.
pi := (interface{})(p)
reflect.ValueOf(&pi).Elem().Field(1).SetString("Dave")
// panic: reflect: call of reflect.Value.Field on interface Value
How can I do this in the general case? Or even better, is there a more idiomatic Go way to accomplish what I'm trying to do?
You need to make switches for the general case, and load the different field types accordingly. This is basic part.
It gets harder when you have slices in the struct (then you have to load them up to the number of elements in the form field), or you have nested structs.
I have written a package that does this. Please see:
http://www.gorillatoolkit.org/pkg/schema
For fun, I tried it out. Note that I cheated a little bit (see comments), but you should get the picture. There is usually a cost to use reflection vs statically typed assignments (like nemo's answer), so be sure to weigh that in your decision (I haven't benchmarked it though).
Also, obvious disclaimer, I haven't tested all edge cases, etc, etc. Don't just copy paste this in production code :)
So here goes:
package main
import (
"fmt"
"reflect"
"strconv"
)
type Person struct {
Age int
Name string
Salary float64
}
// I cheated a little bit, made the map's value a string instead of a slice.
// Could've used just the index 0 instead, or fill an array of structs (obj).
// Either way, this shows the reflection steps.
//
// Note: no error returned from this example, I just log to stdout. Could definitely
// return an array of errors, and should catch a panic since this is possible
// with the reflect package.
func LoadModel(obj interface{}, m map[string]string) {
defer func() {
if e := recover(); e != nil {
fmt.Printf("Panic! %v\n", e)
}
}()
val := reflect.ValueOf(obj)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
// Loop over map, try to match the key to a field
for k, v := range m {
if f := val.FieldByName(k); f.IsValid() {
// Is it assignable?
if f.CanSet() {
// Assign the map's value to this field, converting to the right data type.
switch f.Type().Kind() {
// Only a few kinds, just to show the basic idea...
case reflect.Int:
if i, e := strconv.ParseInt(v, 0, 0); e == nil {
f.SetInt(i)
} else {
fmt.Printf("Could not set int value of %s: %s\n", k, e)
}
case reflect.Float64:
if fl, e := strconv.ParseFloat(v, 0); e == nil {
f.SetFloat(fl)
} else {
fmt.Printf("Could not set float64 value of %s: %s\n", k, e)
}
case reflect.String:
f.SetString(v)
default:
fmt.Printf("Unsupported format %v for field %s\n", f.Type().Kind(), k)
}
} else {
fmt.Printf("Key '%s' cannot be set\n", k)
}
} else {
// Key does not map to a field in obj
fmt.Printf("Key '%s' does not have a corresponding field in obj %+v\n", k, obj)
}
}
}
func main() {
m := map[string]string{
"Age": "36",
"Name": "Johnny",
"Salary": "1400.33",
"Ignored": "True",
}
p := new(Person)
LoadModel(p, m)
fmt.Printf("After LoadModel: Person=%+v\n", p)
}
I'd propose to use a specific interface instead of interface{} in your LoadModel
which your type has to implement in order to be loaded.
For example:
type Loadable interface{
LoadValue(name string, value []string)
}
func LoadModel(loadable Loadable, data map[string][]string) {
for key, value := range data {
loadable.LoadValue(key, value)
}
}
And your Person implements Loadable by implementing LoadModel like this:
type Person struct {
Age int
Name string
}
func (p *Person) LoadValue(name string, value []string) {
switch name {
case "Age":
p.Age, err = strconv.Atoi(value[0])
// etc.
}
}
This is the way, the encoding/binary package or the encoding/json package work, for example.