How should I pass param in golang? - go

I have the follow whole codes:
I hope that string convert map in golang, and use golang reflect.
The following code have simpled from my project.
package main
import (
"encoding/json"
"fmt"
"reflect"
)
func main() {
jsonStr := `{"name": "thinkerou", "age": 31, "balance": 3.14}`
var a map[string]interface{}
var value reflect.Value = reflect.ValueOf(&a)
// call function and pass param
f(jsonStr, value)
// print result
fmt.Println(value.Kind(), value.Interface())
}
func f(v string, value reflect.Value) {
personMap := make(map[string]interface{})
err := json.Unmarshal([]byte(v), &personMap)
if err != nil {
panic(err)
}
value = reflect.Indirect(value)
value = reflect.MakeMap(value.Type())
for k, v := range personMap {
// set key/value
value.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(v))
}
// print result
fmt.Println(value.Kind(), value.Interface())
}
and run it and will get the result:
map map[age:31 balance:3.14 name:thinkerou]
ptr &map[]
I hope the follow result:
map map[age:31 balance:3.14 name:thinkerou]
map map[age:31 balance:3.14 name:thinkerou]
How should I pass reflect.Value param? thanks!

You should be able to get your map from the interface, using type assertion:
a := i.(map[string]interface{})
See "Convert Value type to Map in Golang?"
I have modified your code here.
Note that I don't try to mutate the f(value) argument, but I return it instead.
func f(v string, value reflect.Value) reflect.Value {
...
return value
}
So the code becomes:
value = f(jsonStr, value)
fmt.Println(value.Kind(), value.Interface().(map[string]interface{}))

Related

How to use "reflect" to set interface value inside a struct of struct

Had a rough time trying to set the interface value by using "reflect" package. The interface value is actually inside a struct of a struct. See my code in Go Playground
Basically, inside initProc, I want to assign dummyAFunc function to DummyA field in Box struct
package main
import (
"fmt"
"reflect"
)
type Box struct {
Name string
DummyA interface{}
}
type SmartBox struct {
Box
}
func dummyAFunc(i int) {
fmt.Println("dummyAFunc() is here!")
}
func initProc(inout interface{}) {
// Using "inout interface{}", I can take any struct that contains Box struct
// And my goal is assign dummyAFunc to dummyA in Box struct
iType:=reflect.TypeOf(inout)
iValue:=reflect.ValueOf(inout)
fmt.Println("Type & value:", iType.Elem(), iValue.Elem()) // Type & value: *main.SmartBox &{{ <nil>}}
e := reflect.ValueOf(inout).Elem()
fmt.Println("Can set?", e.CanSet()). // true
fmt.Println("NumField", e.NumField()) // panic: reflect: call of reflect.Value.NumField on ptr Value ?????
fmt.Println("NumMethod", e.NumMethod()) // NumMethod = 0
}
func main() {
smartbox := new (SmartBox)
initProc(&smartbox)
}
I'm new to Go and I've read the The laws of Reflection but still can't figure it out. Please help. Thanks!
You are passing a **SmartBix to initProc. So when you dereference once with reflect's Elem() you are still getting a pointer (*Smart box).
Since new already returns a pointer, just use:
smartbox := new (SmartBox)
// InitProc(smartbox) // **SmartBox
InitProc(smartbox) // *SmartBox
https://play.golang.org/p/j4q6aq6QL_4
EDIT
To update the input struct's DummyA field, you can do something like this:
func initProc2(v interface{}) error {
if reflect.TypeOf(v).Kind() != reflect.Ptr {
return fmt.Errorf("value must be a pointer")
}
dv := reflect.ValueOf(v).Elem()
if dv.Kind() != reflect.Struct {
return fmt.Errorf("value must be a pointer to a struct/interface")
}
const fname = "DummyA" // lookup field name
f := dv.FieldByName(fname)
if !f.CanSet() {
return fmt.Errorf("value has no field %q or cannot be set", fname)
}
nv := reflect.ValueOf(dummyAFunc)
f.Set(nv)
return nil
}
Working example: https://play.golang.org/p/VE751GtSGEw

Write a function to get a slice of string keys from a map no matter what value type the map is

I want to write a function to get all keys from a map as a slice of string which key type is string and the value could be any other type.
Like this but can have any kind of map[string]... as input.
func mapLowCaseKeys(v map[string]string) []string {
keys := make([]string, len(v))
i := 0
for key := range v {
keys[i] = strings.ToLower(key)
i++
}
return keys
}
Actually I want achive Object.keys() in Javascript.
I've tried use map[string]interface{} as the function's paramter type but it can't just pass any specific map to that function, is this possible in golang?
You can make use MapKeys in reflect package to do that(ref: https://golang.org/pkg/reflect/#Value.MapKeys).
MapKeys returns a slice containing all the keys present in the map, in unspecified order. It panics if v's Kind is not Map. It returns an empty slice if v represents a nil map.
An example is given below (playground link: https://play.golang.org/p/xhDtmbGUyz0):
package main
import (
"fmt"
"reflect"
)
func main() {
fmt.Println(mapLowCaseKeys(map[string]float64{
"key1" : 1.2,
}))
fmt.Println(mapLowCaseKeys(map[string]interface{} {
"key1" : 1.2,
"key2" : map[string]string{"kk": "3"},
}))
fmt.Println(mapLowCaseKeys(map[int]float64{
11 : 1.2,
}))
fmt.Println(mapLowCaseKeys(nil))
}
func mapLowCaseKeys(v interface{}) []string {
keys := []string{}
value := reflect.ValueOf(v)
if value.Kind() == reflect.Map {
for _, v := range value.MapKeys() {
if v.Kind() == reflect.String {
keys = append(keys, v.String())
}
}
return keys
} else {
fmt.Println("it is not a map!!")
return keys
}
}

How to properly use variadic args in Golang?

I am complete beginner with Go and I am trying to pass variadic args to encodeit method as a string that will hash the string, otherwise pass an empty string. I wan't to print out hashed string.
I have tried multiple things, but could not get it to work.
package main
import(
"crypto/sha512"
"encoding/hex"
"fmt"
)
func encodeit(content string) string {
sha_512 := sha512.New()
sha_512.Write([]byte(content))
contentH := sha_512.Sum(nil)
contentHash := hex.EncodeToString([]byte(contentH))
return contentHash
}
func some(payload ...string) {
if len(payload) == 1 {
contentHash := encodeit(payload)
} else {
contentHash := encodeit("")
}
return contentHash
}
func main() {
fmt.Println(some(`{"stockSymbol": "TSLA"}`))
}
Here is the error log
# command-line-arguments
.\stackOverflow.go:19:26: cannot use payload (type []string) as type string in argument to encodeit
.\stackOverflow.go:23:2: too many arguments to return
.\stackOverflow.go:23:9: undefined: contentHash
.\stackOverflow.go:27:18: some("{\"stockSymbol\": \"TSLA\"}") used as value
payload becomes an array of strings ([]string) when using an ellipsis (...). It can be iterated on using a key,value for loop:
func printEncoded(payload ...string) {
for i, value := range payload {
fmt.Println(i, encode(value))
}
}
Use printEncoded("TSLA","AMD","DOW") and you won't have to create your own []string array as an argument ([]string{"TSLA","AMD","DOW"}).
You're also going to want to take a look at the JSON package for parsing: {"stockSymbol": "TSLA"}
Fixed Playground
check your func return value:
func some(payload ...string) string
you missed the return type string.

How to make parameter of function is a "generic" struct

For example, I want to write a method like this:
func parseData(rawData []json.RawMessage) []interface{} {
var migrations []interface{}
for _, migration := range rawData {
// this is an custom struct
command := UserCommand{}
json.Unmarshal(migration, &command)
migrations = append(migrations, command)
}
return migrations
}
The problem of this code is: If I don't want to parse UserCommand but any other such as ProductCommand, I must write the same code, only different at line: command := UserCommand{}. So my question is: how can I generic this code.
I have tried this solution but it doesn't work:
func parseData(rawData []json.RawMessage, class interface{}) []interface{} {
var migrations []interface{}
for _, migration := range rawData {
command := class
json.Unmarshal(migration, &command)
migrations = append(migrations, command)
}
return migrations
}
// then I call this method
parseData(data, UserCommand{})
But it doesn't work. It return array of map[string]interface{} How can I fix this.
Edit:
Here is some my defined struct
type UserCommand struct {
User string
Info string
}
type ProductCommand struct {
Name string
Quanlity int
}
// I want to be able to call this method
parseData(data, UserCommand{})
It is possible to support this style of "generic" signature by using Go's reflect package.
package main
import (
"encoding/json"
"fmt"
"reflect"
)
type UserCommand struct {
User string
Info string
}
type ProductCommand struct {
Name string
Quantity int
}
func parseData(rawData []json.RawMessage, class interface{}) []interface{} {
var parsed []interface{}
for _, elem := range rawData {
// Create a pointer to a new zero value of the same type as `class`.
command := reflect.New(reflect.TypeOf(class))
// Pass a pointer to the new value to `json.Unmarshal`.
json.Unmarshal(elem, command.Interface())
// Insert the pointed-to new value into the output slice.
parsed = append(parsed, command.Elem().Interface())
}
return parsed
}
func main() {
data := []json.RawMessage{
json.RawMessage(`{"User":"u1","Info":"i1"}`),
json.RawMessage(`{"User":"u2","Info":"i2"}`),
}
parsed := parseData(data, UserCommand{})
fmt.Printf("%#v\n", parsed)
data = []json.RawMessage{
json.RawMessage(`{"Name":"n1","Quantity":1}`),
json.RawMessage(`{"Name":"n2","Quantity":2}`),
}
parsed = parseData(data, ProductCommand{})
fmt.Printf("%#v\n", parsed)
}
The output shows that the first parseData call has parsed two UserCommand structs and the second call has parsed two ProductCommand structs.
[]interface {}{main.UserCommand{User:"u1", Info:"i1"}, main.UserCommand{User:"u2", Info:"i2"}}
[]interface {}{main.ProductCommand{Name:"n1", Quantity:1}, main.ProductCommand{Name:"n2", Quantity:2}}

Variable modification in golang [Mutability]

The below code opens up a .txt file and counts the word frequencies. I am following a book and I got confused:
My question is here:
filename := os.Args[1]
frequencyForWord := map[string]int{}
updateFrequencies(filename, frequencyForWord)
fmt.Println(frequencyForWord)
I create a variable called frequencyForWord and pass it into a function that does not return anything called func updateFrequencies
This function modifies the variable and that's why when I do fmt.Println(frequencyForWord) it shows me a map that has words as keys and their counts as values.
My question is:
why don't I have to do something like this
frequencyForWord = updateFrequencies(filename, frequencyForWord)
fmt.Println(frequencyForWord)
// And then change func updateFrequencies to something to returns a map
I thought in order for a function to modify a variable I need to pass in the variable as a reference like this updateFrequencies(filename, &frequencyForWord)
Original Code:
package main
import(
"fmt"
"path/filepath"
"os"
"log"
"bufio"
"strings"
"unicode"
)
func main() {
if len(os.Args) == 1 || os.Args[1] == "-h" {
fmt.Printf("usage: %s <file>\n", filepath.Base(os.Args[0]))
os.Exit(1)
}
filename := os.Args[1]
frequencyForWord := map[string]int{}
updateFrequencies(filename, frequencyForWord)
fmt.Println(frequencyForWord)
}
func updateFrequencies(filename string, frequencyForWord map[string]int) string {
file, err := os.Open(filename)
if err != nil {
log.Printf("Failed to open the file: %s.", filename)
}
defer file.Close()
readAndUpdateFrequencies(bufio.NewScanner(file), frequencyForWord)
}
func readAndUpdateFrequencies(scanner *bufio.Scanner, frequencyForWord map[string]int) {
for scanner.Scan() {
for _, word := range SplitOnNonLetter(strings.TrimSpace(scanner.Text())) {
frequencyForWord[strings.ToLower(word)] += 1
}
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
}
func SplitOnNonLetter(line string) []string {
nonLetter := func(char rune) bool { return !unicode.IsLetter(char) }
return strings.FieldsFunc(line, nonLetter)
}
Because the map structure doesn't contain the values itself but points to the structures holding the values.
As written in the documentation :
Like slices, maps hold references to an underlying data structure. If
you pass a map to a function that changes the contents of the map, the
changes will be visible in the caller.
That's just like when you pass a pointer to a function : it lets the function change your value.
Here's another example of the same phenomenon :
type A struct {
b *B
}
type B struct {
c int
}
func incr(a A) {
a.b.c++
}
func main() {
a := A{}
a.b = new(B)
fmt.Println(a.b.c) // prints 0
incr(a)
fmt.Println(a.b.c) // prints 1
}
The function is not modifying the variable, but the value bound to the variable. That's possible because a map is a mutable data structure and passing it to a function does not copy the structure. (A map is implicitly a reference to a hash table and the reference is passed around.)

Resources