How could I write a function to print a map object in Go (Golang)? Right now I have this, but it doesn't compile. It returns cannot convert value (type interface {}) to type reflect.Kind: need type assertion
.
package main
type MyDictionary map[string]interface{}
func (d MyDictionary) String() string {
var stringBuffer bytes.Buffer
for key, value := range d {
stringBuffer.WriteString(key)
stringBuffer.WriteString(": ")
valueType := reflect.Kind(value)
switch valueType {
case reflect.String:
log.Println("string") // just to check if this block gets executed
// Add to stringBuffer
case reflect.Float64:
log.Println("float64") // just to check if this block gets executed
// Add to stringBuffer
default:
log.Println("Error: type was", valueType)
}
}
return stringBuffer.String()
}
func main() {
var dict MyDictionary = make(MyDictionary)
dict["hello"] = "world"
dict["floating"] = 10.0
dict["whole"] = 12
fmt.Println(dict)
}
I want String()
to return a string like hello: world floating: 10.0 whole: 12
. That I can then pass to fmt.Println()
to print this. In Java, I would use StringBuilder
for this.
hello: world
floating: 10.0
whole: 12
I also tried switching on value.(type)
with case string:
and case float64
, but then I didn't know how to write those values to stringBuffer
.
Here's an idiomatic solution.
func (d MyDictionary) String() string {
var buf bytes.Buffer
for k, v := range d {
buf.WriteString(k + ": ")
// v is an interface{} here
switch v := v.(type) {
// The inner v is typed. It shadows the outer interface{} v. That's
// the idiomatic part.
case string:
buf.WriteString(v + "
") // v is a string
case int:
buf.WriteString(fmt.Sprintln(v)) // v is an int
case float64:
buf.WriteString(fmt.Sprintln(v)) // v is a float64
}
}
return buf.String()
}
You need to get the type of the interface and then switch on the kind of the type.
valueType := reflect.TypeOf(value).Kind()
Working Example: http://play.golang.org/p/a-7SePUzZ-
package main
import (
"bytes"
"fmt"
"log"
"reflect"
)
type MyDictionary map[string]interface{}
func (d MyDictionary) String() string {
var stringBuffer bytes.Buffer
for key, value := range d {
stringBuffer.WriteString(key)
stringBuffer.WriteString(": ")
valueType := reflect.TypeOf(value).Kind()
switch valueType {
case reflect.String:
log.Println("string")
default:
log.Println("Type was:", valueType)
}
}
return stringBuffer.String()
}
func main() {
var dict MyDictionary = make(MyDictionary)
dict["hello"] = "world"
dict["floating"] = 10.0
dict["whole"] = 12
fmt.Println(dict)
}
Output
2009/11/10 23:00:00 string
2009/11/10 23:00:00 Type was: float64
2009/11/10 23:00:00 Type was: int
hello: floating: whole:
You can potentially simplify it to this (playground):
func (d MyDictionary) String() string {
var result string
for key, value := range d {
result += fmt.Sprintf("%s: %v
", key, value)
}
return result
}
Which prints:
hello: world
floating: 10
whole: 12
Obviously, the "whole" floating point has the decimals removed (if you set it to 10.5
it will print properly). If that's required, then you'll want to switch on the float
and specify precision as well (playground):
func (d MyDictionary) String() string {
var result string
for key, value := range d {
switch value.(type) {
case float64:
result += fmt.Sprintf("%s: %.2f
", key, value)
default:
result += fmt.Sprintf("%s: %v
", key, value)
}
}
return result
}
Which prints:
floating: 10.00
whole: 12
hello: world