We have a structure like:
type S struct {
M map[string]bool
}
And how can we implement a function like:
// this function modify field by name to a new map
func Modify(s *S, name string, val map[string]bool){
...
}
func Test() {
s := S{
M: map[string]bool{"Hello":true},
}
m := map[string]bool{"World":false}
Modify(&s, "M", m)
}
The reflect package support SetInt/SetString/etc, but none SetMap.
Any way to solve this problem?
Use reflect.Set()
func Modify(s *S, name string, val interface{}) {
rv := reflect.ValueOf(val)
if !rv.IsValid() {
rv = reflect.Zero(reflect.ValueOf(s).Elem().FieldByName(name).Type())
}
reflect.ValueOf(s).Elem().FieldByName(name).Set(rv)
}
Playground
Related
I want to iterate fields of different structures in one array
i try to do it like this
package main
import "fmt"
func main() {
m := msh{"hshHeader"}
p := pid{"pidHeader"}
var i []EmptyInterface
i = append(i, m)
i = append(i, p)
for _, emptyInterface := range i {
fmt.Println(emptyInterface.header)
}
}
type msh struct {
header string
}
type pid struct {
header string
}
type EmptyInterface interface {
}
As expected, on line 15 I get the message "Unresolved reference 'header'".
Do I understand correctly that in Go I should do this using methods
package main
import "fmt"
func main() {
m := msh{"hshHeader"}
p := pid{"pidHeader"}
var i []EmptyInterface
i = append(i, m)
i = append(i, p)
for _, NotEmptyInterface := range i {
fmt.Println(NotEmptyInterface.PrintHeader())
}
}
type msh struct {
header string
}
func (m msh) PrintHeader() string {
return m.header
}
type pid struct {
header string
}
func (p pid) PrintHeader() string {
return p.header
}
type EmptyInterface interface {
PrintHeader() string
}
I don’t know what else to add so that the bot on the site does not throw an error "It looks like your post is mostly code; please add some more details.", I can suggest reading again.
Is there a way to create a generic function that can adjust its operation when passed a map or a slice type vs a basic type?
Goal
Create a slice reading function generator with a flexible return type:
func ValueReader[T <probably something fancy>](i int) func ([]ProtoConvertable) T {
return func (row []ProtoConvertable) T {
return ...
}
}
row := []ProtoConvertable{
&Data[int]{Value: 333},
&ListData{Values: []ProtoConvertable{
&Data[string]{Value: "hello"},
&Data[string]{Value: "world"},
}},
&MapData{Values: map[ProtoConvertable]ProtoConvertable{
&Data[int]{Value: 22}: &Data[string]{Value: "world"},
&Data[int]{Value: 11}: &Data[string]{Value: "hello"},
}},
}
dataReader := ValueReader[int](0) // A function that converts the first element to an int
listDataReader := ValueReader[[]string](1) // A function that converts the second element to a slice
mapDataReader := ValueReader[map[int]string](2) // A function that converts the third element to a map
data := dataReader(row) // 333
listData := listDataReader(row) // []string{"hello", "world"}
mapData := mapDataReader(row) // map[int]string{11: "hello", 22: "world"}
Types
type ValueType interface {
int | string
}
type ProtoConvertable interface {
ToProto() *pb.GenericMessage
}
type Data[T ValueType] struct {
Value T
}
func (d *Data) ToProto() *pb.GenericMessage{
...
}
type ListData struct {
Values []ProtoConvertable
}
func (d *ListData) ToProto() *pb.GenericMessage {
...
}
type MapData struct {
Values map[ProtoConvertable]ProtoConvertable
}
func (d *MapData) ToProto() *pb.GenericMessage {
...
}
Current Solution
func ValueReader[T ValueType](i int) func([]ProtoConvertable) T {
return func(row []ProtoConvertable) T {
return row[i].(*Data[T]).Value
}
}
func ListValueReader[T ValueType](i int) func([]ProtoConvertable) []T {
return func(row []ProtoConvertable) []T {
vs := row[i].(*ListData).Values
res := make([]T, len(vs))
for i, v := range vs {
res[i] = v.(*Data[T]).Value
}
return res
}
}
func MapValueReader[K ValueType, V ValueType](i int) func([]ProtoConvertable) map[K]V {
return func(row []ProtoConvertable) map[K]V {
vs := row[i].(*MapData).Values
res := make(map[K]V, len(vs))
for k, v := range vs {
res[k.(*Data[K]).Value] = v.(*Data[V]).Value
}
return res
}
}
dataReader := ValueReader[int](0)
listDataReader := ListValueReader[string](1)
mapDataReader := MapValueReader[int, string](2)
Note: all of this code is an untested simplification of a more complicated library. It might need some tweaking to get to actually work.
The <probably something fancy> doesn't exist.
The main issue is that you want to model a type parameter that matches a base value and two composite types, one of which is a map type where you want to capture both K and V.
Even if it existed, the body of ValueReader would be a type-switch on T to return each specialized reader function, so your existing solution that involves a small amount of code duplication seems just a better strategy overall.
My advice is to use generics when the operations on the different concrete types of T are really identical. You can read more at: https://go.dev/blog/when-generics
I have multiple structs like bellow
type Person struct {
first string
last string
age int
}
type Product struct {
ProductID int
Name string
Description string
Price float64
}
I want a function that will take a slice of any type as the first argument and a function as a second argument that it will use to call with each element of the slice to construct a string slice and will return that string slice. Something like map() in Typescript/scala or select() in C#.
Since Go doesn't have generics, "any type" can only mean interface{}. You can have something like this:
func ToStringSlice(arr []interface{}, convert func(interface{}) string) []string {
ret := []string{}
for _, elem := range arr {
ret = append(ret, convert(elem))
}
return ret
}
Then you can basically inject any conversion function you want. e.g.
fmt.Println(ToStringSlice([]interface{}{1, 2, "foo", "bar"},
func(x interface{}) string {
return fmt.Sprint(x)
}))
And since string conversions can go bad, I'd also add error checking:
// Define the function with an error return.
type Stringifier func(interface{}) (string, error)
func ToStringSlice(arr []interface{}, convert Stringifier) ([]string, error) {
ret := []string{}
for _, elem := range arr {
if s, e := convert(elem); e != nil {
return nil, e
} else {
ret = append(ret, s)
}
}
return ret, nil
}
one solution involves fmt.Stringer
package main
import (
"fmt"
)
func main() {
items := []fmt.Stringer{Product{ProductID: 1}, Person{first: "first"}}
var res []string
for _, i := range items {
res = append(res, i.String())
}
fmt.Println(res)
}
type Person struct {
first string
last string
age int
}
func (p Person) String() string { return p.first }
type Product struct {
ProductID int
Name string
Description string
Price float64
}
func (p Product) String() string { return fmt.Sprint(p.ProductID) }
That's a typical use case for an interface. Luckily enough the interface you need already exists as fmt.Stringer (https://golang.org/pkg/fmt/#Stringer):
Have a look at https://play.golang.org/p/d1sNPLKhNCU...
First your structs are required to implement the Stringer interface. This is done by simply adding a menthode String(), for example:
type Product struct {
ProductID int
Name string
Description string
Price float64
}
func (p Product) String() string {
return fmt.Sprintf("%d %s", p.ProductID, p.Name)
}
Now your Product is also a Stringer and can be Therefore be passed in any function that accepts Stringers:
func AsStringSlice(in []fmt.Stringer) []string {
out := []string{}
for _, i := range in {
out = append(out, i.String())
}
return out
}
Since "interface{} says nothing" (https://go-proverbs.github.io/) I would recommend to use interface{} as rarely as possible. This is achieved in this approach. Also you can avoid reflection that way.
On the other hand you have to be able to manage the structs, eg. implement the String function. If thats not possible because the Types come from a dependency consider wrapping them:
type MyTypeWithStringer package.ForeinTypeWithoutStringer
func(t MyTypeWithStringer) String() string {
// ...
}
This is a solution that fits me best
type Person struct {
first string
last string
age int
}
type Product struct {
ProductID int
Name string
Description string
Price float64
}
func mapToStringSlice(slice interface{}, mapFunc func(interface{}) string) []string {
s := reflect.ValueOf(slice)
if s.Kind() != reflect.Slice {
panic("mapToStringSlice() given a non-slice type")
}
ret := make([]string, s.Len())
for i:=0; i<s.Len(); i++ {
ret[i] = mapFunc(s.Index(i).Interface())
}
return ret
}
func main() {
persons := []Person{{
first: "A",
last: "g",
age: 20,
},{
first: "B",
last: "r",
age: 40,
},{
first: "C",
last: "",
age: 0,
}}
products := []Product{Product{ProductID: 1}, Product{ProductID: 2}}
personFirstNames := mapToStringSlice(persons, func(input interface{}) string {
return input.(Person).first
})
productIDs := mapToStringSlice(products, func(input interface{}) string {
return input.(Product).ProductID
})
}
I am writing a program which has several structs and functions to handle these structs differently. I am having a generic function which calls the required function based on the inputs. Is there a generic way to use the returned value from getStruct()?
package main
var X func(s []string) A
var Y func(s []string) B
type A struct {
Name string
Place string
}
type B struct {
Name string
Place string
Value string
}
func newA(s []string) A {
a := A{
Name: s[0],
Place: s[1],
}
return a
}
func newB(s []string) B {
a := B{
Name: s[0],
Place: s[1],
Value: s[2],
}
return a
}
func getStruct(t string) interface{} {
switch {
case t == "A":
return X
case t == "B":
return Y
default:
return //someStruct
}
}
func main() {
buildNewStruct := getStruct("A") //Lets assume "A" here is got as an argument
var strSlice = []string{"Bob", "US"}
buildNewStruct(strSlice) //How to do this operation?
//I am hoping to use buildNewStruct(strSlice) to dynamically call
//either of newA(strSlice) or newB(strSlice) function
}
I have tried looking at this and this the later is not exactly the same as my question.
Since I am new to go, I am not sure if something like this is possible.
you can use the reflect package to set the struct properties to the equivalent index positioned value from an []interface{} slice.
package main
import (
"fmt"
"log"
"reflect"
)
func main() {
var a A
err := decode(&a, []interface{}{"Name", "Place"})
log.Println(err)
log.Println(a)
}
func decode(dst interface{}, values []interface{}) error {
rvptr := reflect.ValueOf(dst)
if rvptr.Kind() != reflect.Ptr {
return fmt.Errorf("value must be ptr")
}
rv := rvptr.Elem()
if rv.NumField() < len(values) {
return fmt.Errorf("too many values")
}
if rv.NumField() > len(values) {
return fmt.Errorf("too few values")
}
rvalues := reflect.ValueOf(values)
for i := range values {
f := rv.FieldByIndex([]int{i})
f.Set(rvalues.Index(i).Elem())
}
return nil
}
type A struct {
Name string
Place string
}
type B struct {
Name string
Place string
Value string
}
prints
$ go run main.go
2019/11/21 17:00:17 <nil>
2019/11/21 17:00:17 {Name Place}
The problem is the return type for the function.
func newA(in []string) interface{} {...}
func newB(in []string) interface{} {...}
func getStruct(name string) func([]string) interface{} {
switch name {
case "A": return newA
case "B": return newB
}
return nil
}
func main() {
buildNewStruct := getStruct("A")
var strSlice = []string{"Bob", "US"}
str:=buildNewStruct(strSlice)
if a, ok:=str.(A); ok {
...
}
}
With this approach, even though you saved some code by calling a unified buildNewStruct(), you have to use type assertions to figure out what is returned from that function, so this may not make a lot of sense. It depends on your exact use case though.
What is the way of printing "Foo" here? In this example, what prints is "string".
http://play.golang.org/p/ZnK6PRwEPp
type A struct {
Foo string
}
func (a *A) PrintFoo() {
fmt.Println("Foo value is " + a.Foo)
}
func main() {
a := &A{Foo: "afoo"}
val := reflect.Indirect(reflect.ValueOf(a))
fmt.Println(val.Field(0).Type().Name())
}
You want val.Type().Field(0).Name. The Field method on reflect.Type will return a struct describing that field, which includes the name, among other information.
There is no way to retrieve the field name for a reflect.Value representing a particular field value, since that is a property of the containing struct.
I think the better way to get the fields' name in the struct is
func main() {
a := &A{Foo: "afoo"}
val := reflect.ValueOf(a).Elem()
for i:=0; i<val.NumField();i++{
fmt.Println(val.Type().Field(i).Name)
}
}
There are two tips:
use .Elem() after you reflect.ValueOf(a), because in your case, a is a pointer.
val.Field(i).Type().Name is totally different from val.Type().Field(i).Name. The latter one can get the name of the field in the struct
Hope that it is helpful..
If you want to have a look at more cases, please check my 2mins article
You need to Get the Field of the Type Definition not of the Value.
http://play.golang.org/p/7Bc7MJikbJ
package main
import "fmt"
import "reflect"
type A struct {
Foo string
}
func (a *A) PrintFoo() {
fmt.Println("Foo value is " + a.Foo)
}
func main() {
a := &A{Foo: "afoo"}
val := reflect.Indirect(reflect.ValueOf(a))
fmt.Println(val.Type().Field(0).Name)
}
With the new Names method of the structs package it's even more easier:
package main
import (
"fmt"
"github.com/fatih/structs"
)
type A struct {
Foo string
Bar int
}
func main() {
names := structs.Names(&A{})
fmt.Println(names) // ["Foo", "Bar"]
}
You can also use https://github.com/fatih/structs
// Convert the fields of a struct to a []*Field
fields := s.Fields()
for _, f := range fields {
fmt.Printf("field name: %+v\n", f.Name())
}
package main
import "fmt"
import "reflect"
type A struct {
Foo string
}
func (a *A) PrintFoo() {
fmt.Println("Foo value is " + a.Foo)
}
func main() {
a := &A{Foo: "afoo"}
//long and bored code
t := reflect.TypeOf(*a)
if t.Kind() == reflect.Struct {
for i := 0; i < t.NumField(); i++ {
fmt.Println(t.Field(i).Name)
}
} else {
fmt.Println("not a stuct")
}
//shorthanded call
fmt.Println(reflect.TypeOf(*a).Field(0).Name)//can panic if no field exists
}
You can use this function, which takes the struct as the first parameter, and then its fields. It returns the map type, which is convenient to use
If you use fields from another structure, nothing will happen
If you try to use a different type, it will cause panic
Note that the field has an ordinal number according to the list (starting from 0). All fields in the structure must start with uppercase
func GetStructFieldName(Struct interface{}, StructField ...interface{}) (fields map[int]string) {
fields = make(map[int]string)
s := reflect.ValueOf(Struct).Elem()
for r := range StructField {
f := reflect.ValueOf(StructField[r]).Elem()
for i := 0; i < s.NumField(); i++ {
valueField := s.Field(i)
if valueField.Addr().Interface() == f.Addr().Interface() {
fields[i] = s.Type().Field(i).Name
}
}
}
return fields
}
Full example and playground
package main
import (
"fmt"
"reflect"
)
type Example struct {
Apple bool
Pear int
}
func GetStructFieldName(Struct interface{}, StructField ...interface{}) (fields map[int]string) {
fields = make(map[int]string)
for r := range StructField {
s := reflect.ValueOf(Struct).Elem()
f := reflect.ValueOf(StructField[r]).Elem()
for i := 0; i < s.NumField(); i++ {
valueField := s.Field(i)
if valueField.Addr().Interface() == f.Addr().Interface() {
fields[i] = s.Type().Field(i).Name
}
}
}
return fields
}
func main() {
e := Example{}
names := GetStructFieldName(&e, &e.Apple, &e.Pear)
fmt.Println(names)
fmt.Println(names[0], names[1])
for i := range names {
fmt.Println(names[i])
}
/* Output:
map[0:Apple 1:Pear]
Apple Pear
Apple
Pear
*/
}