84

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) 
   }
}

7 Answers 7

88

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)
        }
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

that's almost exactly what I need, but let's say g needed 2 string parameters. Is that possible?
Could you elaborate on "v.(func(string))"; that seems like it would almost need the reflect package. Do the parens here do any casting, or is it all dealing with the basic interface?
And instead of using f and g as the map values, could you instead have string values there, and instead call f(mapValue) under case "f":?
what if your functions are from a struct type?
82
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{}

3 Comments

All the functions don't have the same signature.
Actually I think this answers my above question; my functions have the same signature. But... here I see the map value is a function with arguments. Interesting, I'll try that as well as passing just the arguments to compare.
Also, say the function returns a string variable, it would look like this: m := map[string]func(string, string)string
24

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")
}

Comments

16

@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).

1 Comment

How to use a type: play.golang.org/p/CtVoOzkT3hw. Good example +1.
3

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")

}

Comments

2

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

Comments

2

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"
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.