I faced such a problem.
I need to compare two structure if they type and name of field is equal.
To assign value from sour to dist. I write some code, but here I can assign reflect.Field() value. Could you help me? And I create the test in the bellow
import (
"reflect"
"testing"
)
func Assign(sour interface{}, dist interface{}) uint {
counter := 0
source := reflect.ValueOf(sour)
target := reflect.ValueOf(dist)
typeSource := reflect.TypeOf(sour)
typeTarget := reflect.TypeOf(dist)
for i:=0; i<source.NumField(); i++{
for j:=0; j<target.NumField();j++{
if (typeSource.Field(i).Type==typeTarget.Field(j).Type && typeSource.Field(i).Name==typeTarget.Field(j).Name){
counter = counter + 1
target.FieldByName(typeSource.Field(i).Name).Set(source.Field(i))
}
}
}
return uint(counter)
}
func TestAssign(t *testing.T) {
type A struct {
A string
B uint
C string
}
type B struct {
AA string
B int
C string
}
var (
a = A{
A: "Тест A",
B: 55,
C: "Test C",
}
b = B{
AA: "OKOK",
B: 10,
C: "FAFA",
}
)
result := Assign(a, b)
switch true {
case b.B != 10:
t.Errorf("b.B = %d; need to be 10", b.B)
case b.C != "Test C":
t.Errorf("b.C = %v; need to be 'Test C'", b.C)
case result != 1:
t.Errorf("Assign(a,b) = %d; need to be 1", result)
}
}
For Assign to work, the second argument must be addressable, i.e. you need to pass a pointer to the struct value.
// the second argument MUST be a pointer to the struct
Assing(source, &target)
Then you need to slightly modify your implementation of Assign since a pointer does not have fileds. You can use the Elem() method to get the struct value to which the pointer points.
func Assign(sour interface{}, dist interface{}) uint {
counter := 0
source := reflect.ValueOf(sour)
// dist is expected to be a pointer, so use Elem() to
// get the type of the value to which the pointer points
target := reflect.ValueOf(dist).Elem()
typeSource := reflect.TypeOf(sour)
typeTarget := target.Type()
for i := 0; i < source.NumField(); i++ {
for j := 0; j < target.NumField(); j++ {
if typeSource.Field(i).Type == typeTarget.Field(j).Type && typeSource.Field(i).Name == typeTarget.Field(j).Name {
counter = counter + 1
target.FieldByName(typeSource.Field(i).Name).Set(source.Field(i))
}
}
}
return uint(counter)
}
Related
package main
import (
"fmt"
)
type ISum interface {
sum() int
}
type SumImpl struct {
Num int
}
func (s SumImpl) sum() int {
return s.Num
}
func main() {
nums := []int{1, 2}
variadicExample1(nums...)
impl1 := SumImpl{Num: 1}
impl2 := SumImpl{Num: 2}
variadicExample2(impl1, impl2)
impls := []SumImpl{
{
Num: 1,
},
{
Num: 2,
},
}
variadicExample2(impls...)
}
func variadicExample1(nums ...int) {
fmt.Print(nums, " ")
total := 0
for _, num := range nums {
total += num
}
fmt.Println(total)
}
func variadicExample2(nums ...ISum) {
fmt.Print(nums, " ")
total := 0
for _, num := range nums {
total += num.sum()
}
fmt.Println(total)
}
I have a question while using variable functions in go language.
When passing a struct that implements an interface as an argument, individual declarations are possible, but can you tell me why it is not possible when passing it through ...?
An error occurs in the code below.
variadicExample2(impls...)
I read this
How to pass an interface argument to a variadic function in Golang?
var impls []ISum
impls = append(impls, impl1)
impls = append(impls, impl1)
variadicExample2(impls...)
I found that the above code is possible.
A SumImpl slice is not a ISum slice. One is a slice of structs, and the other is a slice of interfaces. That's why you cannot pass it to a function that requires a []ISum (i.e. ...ISUm).
But you can do this:
impls := []ISum{
SumImpl{
Num: 1,
},
SumImpl{
Num: 2,
},
}
Description of the problem:
args[0] = ... updates args[0]:
package main
import "fmt"
func MyFunc(lambda any) {
myVars := []any {0}
for i := 0; i < 30; i++ {
lambda.(func(...any))(myVars...)
fmt.Println(myVars[0]) // 0, 2, 4, ..., 60 (good)
}
}
func main() {
MyFunc(func(args ...any) {
args[0] = args[0].(int) + 2
})
}
But when I make variable v := args[0] and attempt to update the value of args[0] by doing v = ..., Go (understandably) reassigns v to a new object rather than updating the value of args[0]:
package main
import "fmt"
func MyFunc(lambda any) {
myVars := []any {0}
for i := 0; i < 30; i++ {
lambda.(func(...any))(myVars...)
fmt.Println(myVars[0]) // 0, 0, 0, ..., 0 (bad)
}
}
func main() {
MyFunc(func(args ...any) {
v := args[0]
v = v.(int) + 2
})
}
My question:
How, using v, can I update args[0]? Any help will be greatly appreciated.
Things I have tried:
I cannot do *v = ..., as this yields compiler error "invalid operation: cannot indirect v (variable of type any)".
I cannot do v := args[0].(*int); *v = *v + 2;, as this yields runtime error "panic: interface conversion: interface {} is int, not *int".
You did not do the pointer operations correctly. Type-assert the pointer variable v. First take the address of the arg value with &, then proceed with rest of your logic.
func main() {
MyFunc(func(args ...any) {
v := &args[0]
*v = (*v).(int) + 2
})
}
Go playground
Consider we have an aggregation struct of interfaces
type Aggregation struct {
a InterfaceA
b InterfaceB
...
n InterfaceN
}
we are trying to make the following function to initialize this struct's fields more funky -- to eliminate the switch:
func (a *Aggregation) Register(i interface{}) *Aggregation {
switch v := i.(type) {
case InterfaceA:
a.a = v
case InterfaceB:
a.a = b
...
case InterfaceN:
a.a = v
}
return a
}
is there any way to accomplish the same functionality with reflection?
This seems to be working
func (a *Aggr) Register2(i interface{}) *Aggr {
v := reflect.ValueOf(a).Elem()
for j := 0; j < v.NumField(); j++ {
f := v.Field(j)
t := f.Type()
if reflect.TypeOf(i).Implements(t) {
f.Set(reflect.ValueOf(i))
break
}
}
return a
}
cc #jimb
I want to take data from DB and write to excel
let's say I have a struct like:
type user struct {
ID int64
Name string
Age int
}
I can get a pointer to slice of user type form DB &[]user{}
but I want to convert that slice to a 2D slice of string [][]string{}
and here's my code try to do such job:
func toStrings(slice interface{}) [][]string {
switch reflect.TypeOf(slice).Elem().Kind() {
case reflect.Slice:
ret := [][]string{}
val := reflect.ValueOf(slice).Elem()
for i := 0; i < val.Len(); i++ {
tempSlice := []string{}
tempV := reflect.ValueOf(val.Index(i))
for j := 0; j < tempV.NumField(); j++ {
tempSlice = append(tempSlice, tempV.Field(j).String())
}
ret = append(ret, tempSlice)
}
return ret
}
return nil
}
But from the code above all I get is a slice like [<*reflect.rtype Value> <unsafe.Pointer Value> <reflect.flag Value>]
where I do it wrong?
my codes in golang playground
sorry, I found where I do it wrong, I got tempV wrong
func toStrings(slice interface{}) [][]string {
switch reflect.TypeOf(slice).Elem().Kind() {
case reflect.Slice:
ret := [][]string{}
val := reflect.ValueOf(slice).Elem()
for i := 0; i < val.Len(); i++ {
tempSlice := []string{}
// tempV should be:
tempV := val.Index(i)
// instead of reflect.ValueOf(val.Index(i))
for j := 0; j < tempV.NumField(); j++ {
tempSlice = append(tempSlice, tempV.Field(j).String())
}
ret = append(ret, tempSlice)
}
return ret
}
return nil
}
There are two problems in the code in the question. The first problem is the slice element is doubled wrapped by a a reflect.Value in the expression reflect.Value(val.Index(i)). Fix by removing the extra call to reflect.Value. The second problem is that the reflect.Value String method does not convert the underlying value to its string representation. Use fmt.Sprint (or one of its friends) to do that.
Try this:
func toStrings(slice interface{}) [][]string {
// Get reflect value for slice. Use Indirect to
// handle slice argument and pointer to slice
// argument.
v := reflect.Indirect(reflect.ValueOf(slice))
if v.Kind() != reflect.Slice {
return nil
}
var result [][]string
// For each element...
for i := 0; i < v.Len(); i++ {
// Get reflect value for slice element (a struct). Use
// Indirect to handle slice of struct and slice of
// pointer to struct.
e := reflect.Indirect(v.Index(i))
if e.Kind() != reflect.Struct {
return nil
}
// Convert fields to string and append.
var element []string
for i := 0; i < e.NumField(); i++ {
// Use fmt.Sprint to convert arbitrary Go value
// to a string.
element = append(element, fmt.Sprint(e.Field(i).Interface()))
}
result = append(result, element)
}
return result
}
Run it on the playground.
Maybe I have a simple way to resolve the problem, golang playground here
I used encoding/json to convert to json data, then convert it to map[string]interface{}.
func toStrings2(slice interface{}) [][]string {
jsonData, _ := json.Marshal(slice)
var out []map[string]interface{}
_ = json.Unmarshal(jsonData, &out)
var fields []string
if len(out) > 0 {
for k := range out[0] {
fields = append(fields, k)
}
}
var ret [][]string
for _, row := range out {
var r []string
for _, k := range fields {
r = append(r, fmt.Sprint(row[k]))
}
ret = append(ret, r)
}
return ret
}
Notice:
With the help of #CeriseLimón, I known that the code in this answer can't handle large values for User.ID.
I have the following code to double the slice.
func doubleSlice(s []int) []int {
t := make([]int, len(s), (cap(s) + 1) * 2 )
for i := range s {
t[i] = s[i]
}
return t
}
I want to make the func to double any type of slice. And I need to know the element type first.
func showInterfaceItem(s interface{}) interface{} {
if reflect.TypeOf(s).Kind() != reflect.Slice {
fmt.Println("The interface is not a slice.")
return
}
var t interface{}
newLen := reflect.ValueOf(s).Len()
newCap := (cap(reflect.ValueOf(s).Cap()) + 1) * 2
t = make([]reflect.TypeOf(s), newLen, newCap)
return t
}
The reflect.TypeOf(s) return the type of interface{}, not the type of element. How can I get the element type of slice interface?
You can use reflect.TypeOf(s).Elem()
to get the type of element of slice.
package main
import (
"fmt"
"reflect"
)
func doubleSlice(s interface{}) interface{} {
if reflect.TypeOf(s).Kind() != reflect.Slice {
fmt.Println("The interface is not a slice.")
return nil
}
v := reflect.ValueOf(s)
newLen := v.Len()
newCap := (v.Cap() + 1) * 2
typ := reflect.TypeOf(s).Elem()
t := reflect.MakeSlice(reflect.SliceOf(typ), newLen, newCap)
reflect.Copy(t, v)
return t.Interface()
}
func main() {
xs := doubleSlice([]string{"foo", "bar"}).([]string)
fmt.Println("data =", xs, "len =", len(xs), "cap =", cap(xs))
ys := doubleSlice([]int{3, 1, 4}).([]int)
fmt.Println("data =", ys, "len =", len(ys), "cap =", cap(ys))
}
The output will be:
data = [foo bar] len = 2 cap = 6
data = [3 1 4] len = 3 cap = 8
Check it in: Go Playground
This is doable in golang and takes me whole day to discover the pattern.
Firstly, we want to get a pointer of slice to make gorm happy, which is has type "*[]Obj". To achieve that in golang, we can create a make wrapper like so:
func makeWrapper(cap uint) interface{} {
arr:= make([]Sth, 0, cap)
return &arr
}
Notice that, we can't directly reference the maked value, which might be the book keeping data need to have a stack space to store.
//Not working example
func makeWrapper(cap uint) interface{} {
return &(make([]Sth, 0, cap))
}
And as the answer before, the reflect.MakeSlice(reflect.SliceOf(typ), 0, capacity).Interface() returns interface{[]Sth}. (the typ here is refer to reflect.TypeOf(Sth{}), which equiv to typ == reflect.TypeOf(v))
Thus we need to create a return object of *[]Sth and the value inside is a slice []Sth with capacity. After understanding the objective, we can have this code:
package main
import (
"reflect"
)
type Sth struct {
a, b string
}
func main() {
af:= createSlice(Sth{})
arr := makeWrapper(10).(*[]Sth)
println(reflect.TypeOf(arr).String())
// equiv to makeWrapper, but we do it via reflection
arr = af(10).(*[]Sth)
println(reflect.TypeOf(arr).String())
}
func makeWrapper(cap uint) interface{} {
arr:= make([]Sth, 0, cap)
return &arr
}
func createSlice(v interface{}) func(int) interface{} {
var typ reflect.Type
if reflect.ValueOf(v).Kind() == reflect.Ptr {
typ = reflect.ValueOf(v).Elem().Type()
} else if reflect.ValueOf(v).Kind() == reflect.Struct {
typ = reflect.TypeOf(v)
} else {
panic("only support instance of struct or pointer of that instance")
}
return func(capacity int) interface{}{
// create the outer object saves our slice
outerObj:=reflect.New(reflect.SliceOf(typ))
// create the slice and save it to return
outerObj.Elem().Set(reflect.MakeSlice(reflect.SliceOf(typ), 0, capacity))
// retrive the interface of outer object
return outerObj.Interface()
}
}