Convert slice of struct to slice of interface - go

How do I convert the following slice of struct to slice of interface?
type BatchImportData struct {
Name string
SetHash []string
SetMembers []string
}
var b []BatchImportData
This is so I can batch import data into Neo4j, as the Neo4j driver requires a slice of interface when the slice of struct is passed as a parameter.
https://github.com/neo4j/neo4j-go-driver
Cypher Type Driver Type
List []interface{}

You can loop over the struct slice and append it to the interface slice.
var b []BatchImportData = []BatchImportData{}
var c []interface{}
for _, b := range b {
c = append(c, b)
}

I think your doubt may be something along the lines of this, in which case you may find this answer quite helpful.
So you will need to loop over the slice and copy each element. Something like:
var b []BatchImportData
interfaceSlice := make([]interface{}, len(b))
for i, v := range b {
interfaceSlice[i] = v
}

Related

How to convert a slice of maps to a slice of structs with different properties

I am working with an api and I need to pass it a slice of structs.
I have a slice of maps so I need to convert it to a slice of structs.
package main
import "fmt"
func main() {
a := []map[string]interface{}{}
b := make(map[string]interface{})
c := make(map[string]interface{})
b["Prop1"] = "Foo"
b["Prop2"] = "Bar"
a = append(a, b)
c["Prop3"] = "Baz"
c["Prop4"] = "Foobar"
a = append(a, c)
fmt.Println(a)
}
[map[Prop1:Foo Prop2:Bar] map[Prop3:Baz Prop4:Foobar]]
so in this example, I have the slice of maps a, which contains b and c which are maps of strings with different keys.
I'm looking to convert a to a slice of structs where the first element is a struct with Prop1 and Prop2 as properties, and where the second element is a struct with Prop3 and Prop4 as properties.
Is this possible?
I've looked at https://github.com/mitchellh/mapstructure but I wasn't able to get it working for my use case. I've looked at this answer:
https://stackoverflow.com/a/26746461/3390419
which explains how to use the library:
mapstructure.Decode(myData, &result)
however this seems to assume that the struct of which result is an instance is predefined, whereas in my case the structure is dynamic.
What you can do is to first loop over each map individually, using the key-value pairs of each map you construct a corresponding slice of reflect.StructField values. Once you have such a slice ready you can pass it to reflect.StructOf, that will return a reflect.Type value that represents the dynamic struct type, you can then pass that to reflect.New to create a reflect.Value which will represent an instance of the dynamic struct (actually pointer to the struct).
E.g.
var result []any
for _, m := range a {
fields := make([]reflect.StructField, 0, len(m))
for k, v := range m {
f := reflect.StructField{
Name: k,
Type: reflect.TypeOf(v), // allow for other types, not just strings
}
fields = append(fields, f)
}
st := reflect.StructOf(fields) // new struct type
sv := reflect.New(st) // new struct value
for k, v := range m {
sv.Elem(). // dereference struct pointer
FieldByName(k). // get the relevant field
Set(reflect.ValueOf(v)) // set the value of the field
}
result = append(result, sv.Interface())
}
https://go.dev/play/p/NzHQzKwhwLH

How do you cast an array of unknown type to type []any? [duplicate]

This question already has answers here:
Type converting slices of interfaces
(9 answers)
In Go, how do I pass a slice of interface to something that expects slice of a different compatible interface? [duplicate]
(1 answer)
How can I cast from []interface{} to []int? [duplicate]
(1 answer)
Cannot use args (type []string) as type []interface {} [duplicate]
(1 answer)
Convert map[interface {}]interface {} to map[string]string
(3 answers)
Closed 7 months ago.
Assuming only arrays are passed as arguments to the arr parameter, I would like each call of unpackArray() to return the argument casted from its original array type to type []any.
package main
func unpackArray(arr any) []any {
return arr.([]any)
}
func main() {
myArr1 := []string {"Hey"}
myArr2 := []int {60}
unpackArray(myArr1)
unpackArray(myArr2)
}
However, this code yields error panic: interface conversion: interface {} is []string, not []interface {}. So it is not allowing me to cast an interface whose static type is not type []any to type []any.
So, given I know that arr's static type is some type of array, and without changing the arr parameter's initialization type from any, how could I convert arr to type []any using this function?
(I am encountering the same problem with maps where I cannot cast from an arbitrary map type to type map[any]any, but I am guessing the solution to this issue would be similar to the solution for arrays.)
Go does not have a builtin "cast" like this, but you can write a function to do it.
You may use reflection to convert a slice of any type to []any:
func unpackArray(s any) []any {
v := reflect.ValueOf(s)
r := make([]any, v.Len())
for i := 0; i < v.Len(); i++ {
r[i] = v.Index(i).Interface()
}
return r
}
You can also use generics in Go 1.18 or later:
func unpackArray[S ~[]E, E any](s S) []any {
r := make([]any, len(s))
for i, e := range s {
r[i] = e
}
return r
}
Both versions of these functions work as requested in the question:
myArr1 := []string {"Hey"}
myArr2 := []int {60}
unpackArray(myArr1)
unpackArray(myArr2)
Notes:
Go does not have "cast" like some other languages. Go has the somewhat related type assertion and conversion features.
The expression arr.([]any) is a type assertion. The expression asserts that the concrete value in the interface arr has type []any. The expression does not do any conversion.
The code in the question uses slices , not arrays as written in the title.
It's not possible to do that directly, because it's not the same thing.
any is the same of interface{} and each interface{} is two-pointers (the first one is the "metadata"/"type-information" and the second one the pointer to the original data).
If you have []uint{60, 42} you have one slice that each element is 8-byte (considering 64bits). So, if you force it to be []any, each element now take 16 bytes, that breaks everything. You can do it using unsafe.
The only way to "cast" is copying the information, so, you can create a new slice of []any and then append each value into that new slice.
One example of copying is:
// You can edit this code!
package main
func unpackArray[T any](arr any) (r []any) {
o := arr.([]T)
r = make([]any, len(o))
for i, v := range o {
r[i] = any(v)
}
return r
}
func main() {
myArr1 := []string{"Hey"}
myArr2 := []int{60}
unpackArray[string](myArr1)
unpackArray[int](myArr2)
}
However, that doesn't make so much sense, since you can use generics in another way.

Converting []*string to []string in golang

i'm new to go.
i have a function that accepts []string as input, but i the input i have to pass is an []*string, how do i convert []*string to []string.
is there any way to convert it using any utilities, or do i have to iterate it using a for-loop and construct an array?
Playground link https://play.golang.org/p/_s2g7-IfGAy
package main
import (
"fmt"
)
func main() {
//Declaring an array of []*string and assigning value to it
var a [1]*string
var strPointer = new(string)
*strPointer = "1"
a[0] = strPointer
fmt.Println(*a[0])
// accept(a) this wont work
//Converting to array []string
var b []string
for i := range a {
b = append(b, *a[i])
}
accept(b)// this works
fmt.Println(b)
}
func accept(param []string) {
fmt.Println("Works!")
}
Your accept(param []string) expect a slice of string.
var a [1]*string This declares Go array with a length of 1. So it's not a slice.
You can declare an empty slice of string pointers using this. var a []*string
And you have to iterate through the array and make a slice with the value of pointer elements and call the accept with that slice.
Example function to convert []*string to []string
func stringer(str []*string) []string{
var strs []string
for _, s := range str {
if s == nil {
strs = append(strs, "")
continue
}
strs = append(strs, *s)
}
return strs
}
run here
how do i convert []*string to []string
You cannot. This kind of type conversion not possible in Go.
is there any way to convert it using any utilities [...]
Asking for 3rd party libraries/tools/packages is OT on SO.
[...] or do i have to iterate it using a for-loop and construct an array
This is the only clean, normal, "idiomatic" way of doing this.

Interface conversion in slice of slice

I wrote this example code (https://play.golang.org/p/u_oz5X4aU07):
func main() {
var obj interface{}
json.Unmarshal([]byte("[[1,2],[3,4]]"), &obj)
val := obj.([][]int)
fmt.Println(val)
}
Why I get the error:
interface conversion: interface {} is []interface {}, not [][]int
Is there a simple way to transform obj in a slice of slice?
This code works, but I'd like something more compact and efficient.
var val [][]float64
for r, v := range obj.([]interface{}) {
val = append(val,nil)
for _, w := range v.([]interface{}) {
val[r] = append(val[r], w.(float64))
}
}
No, ultimately you'll have to loop over the two slices!
You can read here why you can't just use one as the other:
https://research.swtch.com/interfaces
This answer might also be useful:
Why golang struct array cannot be assigned to an interface array
Essentially it's because the interface is stored as a 2 word pair, one defining the type and one the values.
You have to manually convert to the required type by visiting all the values in for-range loops.

Why Golang object property of byte array will be wiped when assigned to another variable

We need to wipe out some variables after use. But it seems really weird when it's assigned with a []byte field in a struct.
Why this assignment of []byte is not a copy but a pointer?
What should I do to keep the value in struct a.bs, but wipe out the b as local variable?
http://play.golang.org/p/MT_wAHj2OM
package main
import "fmt"
type so struct {
bs []byte
}
func zeroes(n int) []byte {
return make([]byte, n)
}
func wipeBytes(b []byte) {
copy(b, zeroes(len(b)))
}
func main() {
a := so{bs: []byte{0x01, 0x02}}
b := a.bs
wipeBytes(b)
fmt.Println(b) //b == []byte{}
fmt.Println(a.bs) //a.bs == []byte{}
}
Slices are inherently reference-y things. Assigning one doesn't copy its contents. You can think of a slice value as being a "slice head" structure, which contains a pointer to the slice's underlying array, and the offset and length of the slice within the array. It's this structure that's copied when you copy the slice, not any of the values in the array.
You can do
b := make([]byte, len(a.bs)))
copy(b, a.bs)
to make b a new slice and copy a.bs's contents into it. Then nothing you do to one will have any effect on the other.
When declaring/creating the 'array' ([]byte{0x01, 0x02}), you're not specifying a length ([2]byte{0x01, 0x02}), which means that it's a slice instead of an array. And slices objects internally contains a pointer to it's content.
func ObjectAssign(target interface{}, object interface{}) {
// object atributes values in target atributes values
// using pattern matching (https://golang.org/pkg/reflect/#Value.FieldByName)
// https://stackoverflow.com/questions/35590190/how-to-use-the-spread-operator-in-golang
t := reflect.ValueOf(target).Elem()
o := reflect.ValueOf(object).Elem()
for i := 0; i < o.NumField(); i++ {
for j := 0; j < t.NumField(); j++ {
if t.Field(j).Name() == o.Field(i).Name() {
t.Field(j).Set(o.Field(i))
}
}
}
}

Resources