Go map of functions - dictionary

I have Go program that has a function defined. I also have a map that should have a key for each function. How can I do that?
I have tried this, but this doesn't work.
func a(param string) {
}
m := map[string] func {
'a_func': a,
}
for key, value := range m {
if key == 'a_func' {
value(param)
}
}

Are you trying to do something like this? I've revised the example to use varying types and numbers of function parameters.
package main
import "fmt"
func f(p string) {
fmt.Println("function f parameter:", p)
}
func g(p string, q int) {
fmt.Println("function g parameters:", p, q)
}
func main() {
m := map[string]interface{}{
"f": f,
"g": g,
}
for k, v := range m {
switch k {
case "f":
v.(func(string))("astring")
case "g":
v.(func(string, int))("astring", 42)
}
}
}

m := map[string]func(string, string)
Works if you know the signature (and all the funcs have the same signature)
I think this is cleaner/safer than using interface{}

You can define a type if functions are same interface.
package main
import "log"
type fn func (string)
func foo(msg string) {
log.Printf("foo! Message is %s", msg)
}
func bar(msg string) {
log.Printf("bar! Message is %s", msg)
}
func main() {
m := map[string] fn {
"f": foo,
"b": bar,
}
log.Printf("map is %v", m)
m["f"]("Hello")
m["b"]("World")
}

#Seth Hoenig's answer helped me best, but I just wanted to add that Go accepts functions with defined return value as well:
package main
func main() {
m := map[string]func(string) string{
"foo": func(s string) string { return s + "nurf" },
}
m["foo"]("baz") // "baznurf"
}
If you think it's ugly, you could always use a type (see #smagch's answer).

I used a map[string]func (a type, b *type) I passed a string to search the map and a pointer to modify the slice.
Hope that helps!
var Exceptions map[string]func(step string, item *structs.Item)
func SetExceptions() {
Exceptions = map[string]func(a string, i *structs.Item){
"step1": step1,
}
}
func RunExceptions(state string, item *structs.Item) {
method, methBool := Exceptions[state]
if methBool {
method(state, item)
}
}
func step1(step string, item *structs.Item) {
item.Title = "Modified"
}

Here is the way I made it work in my case:
package main
import (
"fmt"
)
var routes map[string]func() string
func main() {
routes = map[string]func() string{
"GET /": homePage,
"GET /about": aboutPage,
}
fmt.Println("GET /", pageContent("GET /"))
fmt.Println("GET /about", pageContent("GET /about"))
fmt.Println("GET /unknown", pageContent("GET /unknown"))
// Output:
// GET / Home page
// GET /about About page
// GET /unknown 404: Page Not Found
}
func pageContent(route string) string {
page, ok := routes[route]
if ok {
return page()
} else {
return notFoundPage()
}
}
func homePage() string {
return "Home page"
}
func aboutPage() string {
return "About page"
}
func notFoundPage() string {
return "404: Page Not Found"
}
https://play.golang.org/p/8_g6Di1OKZS

Hope this works for you(you can use interface{} instead any)
package main
import (
"fmt"
)
func toon(v any) {
fmt.Println(v)
}
func main() {
names := map[string]any{
"Function": toon,
}
names["Function"].(func(any))("a")
}

Related

Getting the map value with Golang

I couldn't do it because I didn't fully understand the logic, but you can look at my code example and output below. I just want to return the country name as a variable in the output.
Json File : https://gist.githubusercontent.com/coderantidote/0894cf7c5204d4c712207ff9162d044d/raw/ab9ec19dcfecd93addb4b1961a2506b34164c090/tld
package main
import (
"fmt"
"strings"
tld "github.com/jpillora/go-tld"
gojsonq "github.com/thedevsaddam/gojsonq/v2"
)
func main() {
urls := []string{
"http://google.com",
"https://eduuk.co.uk/",
"https://www.medi-cal.ca.gov/",
}
for _, url := range urls {
u, _ := tld.Parse(url)
jq := gojsonq.New().File("./tld.json").From("tlds")
tldSlice := strings.Split(u.TLD, ".")
if len(tldSlice) == 2 {
jq.Where("fields.tld", "=", tldSlice[1]).Select("fields.country")
} else {
jq.Where("fields.tld", "strictContains", tldSlice[0]).Select("fields.country")
}
fmt.Printf("%s\n", u.TLD)
fmt.Printf("%v\n", jq.Get())
}
}
Output:
com
[map[country:Commercial organizations]]
co.uk
[map[country:United Kingdom]]
gov
[map[country:US government]]
Is this what you are looking for?
fmt.Printf("%s\n", u.TLD)
fmt.Printf("%v\n", jq.First().(map[string]interface{})["country"])

Marshalling a map with struct as key

I have a struct where one of the fields is a map with the key being another struct. I am using Go version 16.1 and the map created that way is supposedly supported in this version (albeit unsure when field is a map). Here is the relevant code and test that fails with unsupported type error.
package model
import (
"encoding/json"
slog "github.com/go-eden/slf4go"
)
type User struct{
ID string `json:"id" bson:"_id,omitempty"`
FName string `json:"fName"`
MName string `json:"mName"`
LName string `json:"lName"`
Jobs map[*Job]float64 `json:"jobs,omitempty"`
Password string `json:"password"`
IsAdmin bool `json:"isAdmin"`
}
func (u *User)IsModel()bool{
return true
}
func(u * User)ToJSON()string{
//var jsonUser string;
b,err := json.Marshal(u)
if err !=nil {
slog.Info(err)
return err.Error()
}
return string(b)
}
func (u * User)FromJSON(jsonString string)bool{
err := json.Unmarshal([]byte(jsonString),u)
if err != nil{
return false
}
return true
}
and the struct job is:
package model
import "encoding/json"
type Job struct{
ID string `json:"id" bson:"_id,omitempty"`
Name string `json:"name"`
}
func(j *Job)IsModel()bool{
return true
}
func(j *Job) ToJSON()string{
b,err := json.Marshal(j)
if err !=nil {
return err.Error()
}
return string(b)
}
func(j *Job) FromJSON(jsonString string)bool{
err := json.Unmarshal([]byte(jsonString),j)
if err != nil{
return false
}
return true
}
Finally the test that fails is the following (marshalling fails after I add the job to the jobs map):
package model
import (
"fmt"
"reflect"
"testing"
)
func TestUser_ToJSON(t *testing.T) {
user := &User{
ID: "",
FName: "Mike",
MName: "",
LName: "Clovis",
Jobs: make(map[*Job]float64),
Password: "xyz",
IsAdmin: true,
}
newUser := &User{}
fmt.Println(user.ToJSON())
job := &Job{
ID: "1",
Name: "Cashier",
}
user.Jobs[job] = 12.99
fmt.Println("User as json")
fmt.Println(user.ToJSON())
newUser.FromJSON(user.ToJSON())
fmt.Println(newUser)
if reflect.DeepEqual(user,newUser) {
fmt.Println(newUser)
}else{
fmt.Println(user)
fmt.Println(newUser)
t.Error("Cannot marshal and unmarshal User struct")
}
dUser := *user
fmt.Println(dUser)
}
By my reading of the documentation this should work! If anyone has a suggestion or workaround I would appreciate it. As an FYI I have tried making the map with the key being both a job and as a pointer to the job with the same results.

Navigation into a map with a string path variable for golang

I would like to directly navigate to a value in a map. Lets be more specific with the following go code example which should give me the value of "Walter" directly: (https://play.golang.org/p/tYJsvp39hn)
type Signature struct{
Name string
Signed bool
}
path := "Document.Signatures.1.Name"
map := map[string]interface{}{
"Document": map[string]interface{}{
"Signatures": []interface{}{
Signature{ Name: "Hugo", Signed: false },
Signature{ Name: "Walter", Signed: false },
},
"Otherstuff": "asadwa",
},
"AlsoOtherStuff": "adwaw",
}
// map.giveMe(path)
// even better (if possible:) map.change(path,"ToThisNewValue")
I have searched for solutions, but I can't find any on the internet. Maybe one of you knows how to do this or knows a library to use for me.
Thank you so much in advance!
Quite a lot of reflect calls will be needed if there is no predefined struct.
That being said, you can do it by iterating through the map with type checking on every iteration and handling cases accordingly.
// Splitting the path into keys
keys := strings.Split(path, ".")
var value interface{} = map1
for _, key := range keys {
if value, err = Get(key, value); err != nil {
break
}
}
if err == nil {
fmt.Println("Value:", value)
} else {
fmt.Println("Error:", err)
}
func Get(key string, s interface{}) (v interface{}, err error) {
var (
i int64
ok bool
)
switch s.(type) {
case map[string]interface{}:
if v, ok = s.(map[string]interface{})[key]; !ok {
err = fmt.Errorf("Key not present. [Key:%s]", key)
}
case []interface{}:
if i, err = strconv.ParseInt(key, 10, 64); err == nil {
array := s.([]interface{})
if int(i) < len(array) {
v = array[i]
} else {
err = fmt.Errorf("Index out of bounds. [Index:%d] [Array:%v]", i, array)
}
}
case Signature:
r := reflect.ValueOf(s)
v = reflect.Indirect(r).FieldByName(key)
}
//fmt.Println("Value:", v, " Key:", key, "Error:", err)
return v, err
}
Playground code

How do I convert a Handle to a HandleFunc?

I am making a captcha and am following the example given here. I need to modify the example to use gorilla mux's routing as the rest of my app uses that. For the life of me I can't figure out how to correctly route the path for line 47 of that example. What I have below results in no captcha generated...(the example itself works fine). For shits & giggles I've even tried "http.HandleFunc("/captcha/", captchaHandler)" but that doesn't work either. Any suggestions?
package main
import (
"github.com/dchest/captcha"
"github.com/gorilla/mux"
"io"
"log"
"net/http"
"text/template"
)
var formTemplate = template.Must(template.New("example").Parse(formTemplateSrc))
func showFormHandler(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.NotFound(w, r)
return
}
d := struct {
CaptchaId string
}{
captcha.New(),
}
if err := formTemplate.Execute(w, &d); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func processFormHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
if !captcha.VerifyString(r.FormValue("captchaId"), r.FormValue("captchaSolution")) {
io.WriteString(w, "Wrong captcha solution! No robots allowed!\n")
} else {
io.WriteString(w, "Great job, human! You solved the captcha.\n")
}
io.WriteString(w, "<br><a href='/'>Try another one</a>")
}
func captchaHandler(w http.ResponseWriter, r *http.Request) {
captcha.Server(captcha.StdWidth, captcha.StdHeight)
}
type Routes []Route
type Route struct {
Method string
Pattern string
HandlerFunc http.HandlerFunc
}
func main() {
/*
http.HandleFunc("/", showFormHandler)
http.HandleFunc("/process", processFormHandler)
//http.HandleFunc("/captcha/", captchaHandler) // doesn't work
http.Handle("/captcha/", captcha.Server(captcha.StdWidth, captcha.StdHeight))
fmt.Println("Server is at localhost:8666")
if err := http.ListenAndServe(":8666", nil); err != nil {
log.Fatal(err)
}
*/
var routes = Routes{
Route{"GET", "/", showFormHandler},
Route{"POST", "/process", processFormHandler},
Route{"GET", "/captcha/", captchaHandler},
}
router := mux.NewRouter().StrictSlash(true)
for _, route := range routes {
var handler http.Handler
handler = route.HandlerFunc
router.Methods(route.Method).Path(route.Pattern).Handler(handler)
}
//router.Methods("GET").Path("/captcha/").HandlerFunc(captcha.Server(captcha.StdWidth, captcha.StdHeight))
port := ":8666"
log.Println("Listening at", port)
log.Fatal(http.ListenAndServe(port, router))
}
const formTemplateSrc = `<!doctype html>
<head><title>Captcha Example</title></head>
<body>
<script>
function setSrcQuery(e, q) {
var src = e.src;
var p = src.indexOf('?');
if (p >= 0) {
src = src.substr(0, p);
}
e.src = src + "?" + q
}
function playAudio() {
var le = document.getElementById("lang");
var lang = le.options[le.selectedIndex].value;
var e = document.getElementById('audio')
setSrcQuery(e, "lang=" + lang)
e.style.display = 'block';
e.autoplay = 'true';
return false;
}
function changeLang() {
var e = document.getElementById('audio')
if (e.style.display == 'block') {
playAudio();
}
}
function reload() {
setSrcQuery(document.getElementById('image'), "reload=" + (new Date()).getTime());
setSrcQuery(document.getElementById('audio'), (new Date()).getTime());
return false;
}
</script>
<select id="lang" onchange="changeLang()">
<option value="en">English</option>
<option value="ru">Russian</option>
<option value="zh">Chinese</option>
</select>
<form action="/process" method=post>
<p>Type the numbers you see in the picture below:</p>
<p><img id=image src="/captcha/{{.CaptchaId}}.png" alt="Captcha image"></p>
Reload | Play Audio
<audio id=audio controls style="display:none" src="/captcha/{{.CaptchaId}}.wav" preload=none>
You browser doesn't support audio.
Download file to play it in the external player.
</audio>
<input type=hidden name=captchaId value="{{.CaptchaId}}"><br>
<input name=captchaSolution>
<input type=submit value=Submit>
</form>
`
EDIT #1:
To be clearer "doesn't work" isn't helpful. It returns a 404 error.
EDIT #2:
The example on github works fine....its only when I modify the route that it returns a 404 when I try to generate a captcha.
You can convert the http.Handler h to a http.HandlerFunc using the method expression:
h.ServeHTTP
Instead of converting to a HandlerFunc, you can register the Handler directly using the route Handler method:
router.Methods("GET").Path("/captcha/").Handler(captcha.Server(captcha.StdWidth, captcha.StdHeight))
Based on your comments and edits, I think you want a prefix match instead of an exact match:
router.Methods("GET").PathPrefix("/captcha/").Handler(captcha.Server(captcha.StdWidth, captcha.StdHeight))
The direct answer to a topic question is simply as is.
func(w http.ResponseWriter, r *http.Request, p map[string]string) {
youhttp.Handler().ServeHTTP(w, r)
}

Reflect value of []byte

How do I retrieve the []byte value of this interface?
package main
import (
"reflect"
)
func byteInterface() interface{} {
return []byte("foo")
}
func main() {
//var b []byte
i := byteInterface()
switch {
case reflect.TypeOf(i).Kind() == reflect.Slice && (reflect.TypeOf(i) == reflect.TypeOf([]byte(nil))):
default:
panic("should have bytes")
}
}
You can use a type assertion for this; no need to use the reflect package:
package main
func byteInterface() interface{} {
return []byte("foo")
}
func main() {
i := byteInterface()
if b, ok := i.([]byte); ok {
// use b as []byte
println(len(b))
} else {
panic("should have bytes")
}
}

Resources