func main() {
strSlice := []string{"a", "b", "c"}
f(strSlice)
}
func f(slice interface{}) {
anySlice, isSlice := slice.([]interface{})
fmt.Printf("isSlice = %t, anySlice = %#v\n", isSlice, anySlice)
}
Playground: https://play.golang.org/p/UN25mIOqmOd
This program prints isSlice = false, anySlice = []interface {}(nil). Why is that?
I would have expected this type asssertion to be possible.
And: Is there a way to dynamically check that interface{} is a slice of something?
The type assertion fails because a []string is not an []interface{}. See the FAQ for details.
Use the reflect package to determine if the concrete value in an interface is a slice:
func f(slice interface{}) {
isSlice := reflect.ValueOf(slice).Kind() == reflect.Slice
fmt.Printf("isSlice = %t, anySlice = %#v\n", isSlice, slice)
}
Run it on the playground.
You can also use the reflect package to iterate through the values:
v := reflect.ValueOf(slice)
isSlice := v.Kind() == reflect.Slice
if isSlice {
for i := 0; i < v.Len(); i++ {
fmt.Printf("%d: %v\n", i, v.Index(i).Interface())
}
}
Run it on the playground.
This program prints isSlice = false, anySlice = []interface {}(nil). Why is that?
because slice doesn't contain a []interface{}, it contains a []string. Those are different types, and Go doesn't give you any notion of covariant container types.
And: Is there a way to dynamically check that interface{} is a slice of something?
Yes, you can use reflection:
func f(slice interface{}) {
typ := reflect.TypeOf(slice)
if typ.Kind() == reflect.Slice {
elemType := typ.Elem()
fmt.Println("slice of", elemType.Name())
} else {
fmt.Println("not a slice")
}
}
Actually doing anything with that information may be more involved.
Notice that if you change your assertion to: slice.([]string) things will work as expected. This makes sense when you consider how Go handles string to interface{} type assertions. The following will result in a compilation error:
package main
func main() {
s := "some string"
i := s.(interface{})
}
Error:
invalid type assertion: s.(<inter>) (non-interface type string on left)
It makes sense that going from []string to []interface{} should also fail since Go does not treat strings as interface{} types.
Related
For any given struct, I want to loop over its fields and set any nil slices to an empty slice. However, slices are unaddressable and hence not settable. How can I set the values of any nil slices?
Example:
https://goplay.space/#iV6OHkYVTru
package main
import (
"fmt"
"reflect"
)
type Foo struct {
IntSlice []int
StrSlice []string
}
func main() {
foo := Foo{}
fmt.Println(foo.IntSlice == nil)
initNilSlices(foo)
fmt.Println(foo.IntSlice == nil)
}
func initNilSlices(x interface{}) {
v := reflect.ValueOf(x)
for i := 0; i < v.NumField(); i++ {
f := v.Field(i)
if f.Kind() == reflect.Slice {
t := f.Type()
f.Set(reflect.MakeSlice(t, 0, 0))
}
}
}
You can't update / modify foo in initNilSlices() if you pass foo because that will be a copy, and you could only modify the copy (and not the original value).
This is what the error message "hints":
panic: reflect: reflect.Value.Set using unaddressable value
You obtained the reflect.Value from a non-pointer value, and even what reflect.ValueOf() gets is a copy, so it doesn't allow you to modify it because it wouldn't be what you'd want.
You have to pass the address of foo:
initNilSlices(&foo)
And in initNilSlices() use Value.Elem():
v := reflect.ValueOf(x).Elem()
With this it works and outputs (try it on the Go Playground):
true
false
I am new to Go & I am trying to learn how to cast interface{} to a Map. Here is an example of what I am trying to implement.
Playground link : https://play.golang.org/p/3jhKlGKO46Z
Thanks for the help.
package main
import (
"fmt"
)
func main() {
Map := make(map[string]interface{})
test(Map)
for k,v := range Map {
fmt.Println("key : %v Value : %v", k, v)
}
}
func test(v interface{}) error{
data := make(map[int]interface{})
for i := 0; i < 10; i++ {
data[i] = i * 5
}
for key,val := range data {
// fmt.Println("key : %v Value : %v", key, val)
v[key] = val
}
return nil
Go supports type assertions for the interfaces. It provides the concrete value present in the interface.
You can achieve this with following code.
m, ok := v.(map[int]interface{})
if ok {
// use m
_ = m
}
If the asserted value is not of given type, ok will be false
If you avoid second return value, the program will panic for wrong assertions.
I strongly recommend you to go through https://tour.golang.org
My input is an interface{}, and I know it can be an array of any type.
I'd like to read one of the elements of my input, so I try to convert my interface{} into an []interface{}, but go will give me the following error:
panic: interface conversion: interface {} is []map[string]int, not []interface {}
How can I do that conversion? (without reflect if possible).
Playground test
Thanks
The solution involving the reflect package.
package main
import (
"fmt"
"reflect"
)
func main() {
var v interface{} = []string{"a", "b", "c"}
var out []interface{}
rv := reflect.ValueOf(v)
if rv.Kind() == reflect.Slice {
for i := 0; i < rv.Len(); i++ {
out = append(out, rv.Index(i).Interface())
}
}
fmt.Println(out)
}
// Output:
// [a b c]
I'm actually working on this right now as my issue involves taking something from a json object (map[string]interface{}) which may or may not contain a particular key ({"someKey": [a, b, c, ...]) and if it does contain that key then we want to take that (which will necessarily be interface{} type) and convert it to []interface{}. The method I've found so far is to use json marshall/unmarshall. This seems a little hacky to me, will update if I find a more elegant solution. Til then, you can have my method:
https://play.golang.org/p/4VAwQQE4O0b
type a map[string]interface{}
type b []string
func main() {
obj := a{
"someKey": b{"a", "b", "c"},
}
if obj["someKey"] != nil { // check the value exists
var someArr []interface{}
//marshal interface to byte and then unmarshal to []interface{}
somebytes, _ := json.Marshal(obj["someKey"])
err := json.Unmarshal(somebytes, &someArr)
if err != nil {
fmt.Println("Error in unmarshal")
}
fmt.Println(someArr)
}
}
How can I do that conversion? (without reflect if possible).
Please consider type switches.
Reflection is expensive.
func toSlice(i interface{}) []interface{} {
var out []interface{}
switch v := i.(type) {
case []interface{}:
for x := 0; x < len(v); x++ {
out = append(out, v[x])
}
default:
fmt.Printf("invalid type: %T\n", v)
}
return out
}
The point of the interface is to define the behaviour you want to use, if you use an empty interface, you know nothing about the types in that slice.
If you want to print it, you can use println or printf with no conversion.
If you want to access it, and must allow any type, you can use reflect (slow and complex to use).
If you want to acess it, and use common behaviour/ data that you can define functions for, define an interface, e.g. :
type Doer interface {
Do() error
}
parentStruct := []Doer{...}
testStruct.Do()
If none of that works, wait for Go 2 and generics.
For anyone finding this in 2022, now that we have generics you can do it like this:
func convertSlice[T any](data []T) []interface{} {
output := make([]interface{}, len(data))
for idx, item := range data {
output[idx] = item
}
return output
}
I think what you are looking is type assertion
package main
import (
"fmt"
)
func main() {
parentStruct := map[string]interface{}{
"test": []map[string]int{
{"a": 1, "b": 2},
{"c": 3},
},
}
testStruct := parentStruct["test"].([]map[string]int)
fmt.Println(testStruct)
}
read this link: https://golang.org/ref/spec#Type_assertions
https://play.golang.org/p/81uL2hgrN3l
Can someone explain why this doesn't work and how can we return slices of interfaces, []interface{}, from functions like showed in the example?
import (
"fmt"
)
func main() {
var test []string
Test(&test)
fmt.Println(test)
}
func Test(t interface{}) {
a := []interface{}{"first", "second"}
fmt.Println(a)
t = a
}
Example of running code can be found here:
https://play.golang.org/p/vcEGHSdWrjv
BTW, this is the func I'm trying to extract data from: https://godoc.org/github.com/mongodb/mongo-go-driver/mongo#Collection.Distinct
Note: the type we expect is not always of type []string, I'm just using string as an example here.
Thanks!
In you example, you are not trying to "return" a slice but rather you seem to be looking to modify the argument to point to a new slice of strings.
The way you are doing it does not work cause in Go, arguments are passed by value.
When you do this:
t = a
t is a copy of the &test you are sending to the function as an argument.
So modifying t does not change your test variable.
You need to pass in the address of a pointer in order to be able to modify what the pointer points to.
Try this way:
func main() {
var test *[]string
Test(&test)
fmt.Println(*test)
}
func Test(t interface{}) {
a := []string{"first", "second"}
fmt.Println(a)
*t.(**[]string) = &a
}
Output:
[first second]
[first second]
Playground: https://play.golang.org/p/tliMrmliykp
Because an []string and a []interface{} are different types, you cannot assign one to the other.
You must copy the slice to convert []interface{} to a slice of some specific type. If you know that the []interface{} always contains string values, then use the following:
func stringSlice() []string {
a := []interface{}{"first", "second"} // query result
fmt.Println(a)
result := make([]string, len(a))
for i := range a {
var ok bool
result[i], ok = a[i].(string)
if !ok {
// handle error with unexpected type
}
}
return result
}
If the result can have arbitrary element types, then use reflection to copy the slice:
func anySlice(result interface{}) {
a := []interface{}{"first", "second"} // query result
slice := reflect.ValueOf(result).Elem()
elementType := slice.Type().Elem()
for _, v := range a {
rv := reflect.ValueOf(v)
if !rv.Type().AssignableTo(elementType) {
// handle error with unexpected type
}
slice.Set(reflect.Append(slice, rv))
}
}
Use it like this:
var s []string
anySlice(&s)
playground example
I'm parsing a JSON object which contains an array of strings :
var ii interface{}
json := "{\"aString\": [\"aaa_111\", \"bbb_222\"], \"whatever\":\"ccc\"}"
err := json.Unmarshal([]byte(json), &ii)
if err != nil {
log.Fatal(err)
}
data := ii.(map[string]interface{})
fmt.Println(data["aString"]) // outputs: ["aaa_111" "bbb_222"]
I tried to convert data["aString"] to []string to be able to loop over it, but it fails :
test := []string(data["aString"]).([]string)
fmt.Println(test) // panic -> interface conversion:
// interface is string, not []string
How can I convert data["aString"] ?
edit:
I didn't express myself properly. If I print data, I have such map :
map[aString:["BBB-222","AAA-111"] whatever:ccc]
I want to loop over aString (to manipule each array entry). But I can't find how, because aString is type interface {} :
for i, v := range aString { // <-- fails
// ...
fmt.Println(i, v)
}
That's why I want to convert aString. I don't want to convert a string which looks like an array to an array.
I recommend you move away from this implementation in general. Your json may vary but you can easily use objects and avoid all this type unsafe nonsense.
Anyway, that conversion doesn't work because the types inside the slice are not string, they're also interface{}. You have to iterate the collection then do a type assertion on each item like so:
aInterface := data["aString"].([]interface{})
aString := make([]string, len(aInterface))
for i, v := range aInterface {
aString[i] = v.(string)
}
Is it what you need?
package main
import (
"fmt"
"encoding/json"
)
func main() {
js := "{\"aString\": [\"aaa_111\", \"bbb_222\"], \"whatever\":\"ccc\"}"
a := make(map[string]interface{})
json.Unmarshal([]byte(js), &a)
for _, v := range a["aString"].([]interface{}) {
str := v.(string)
fmt.Println(str)
}
}
Check on Go Playground
For another approach, you can use a struct instead:
package main
import (
"encoding/json"
"fmt"
)
func main() {
s := []byte(`{"aString": ["aaa_111", "bbb_222"], "whatever":"ccc"}`)
var t struct {
Astring []string
Whatever string
}
json.Unmarshal(s, &t)
fmt.Printf("%+v\n", t) // {Astring:[aaa_111 bbb_222] Whatever:ccc}
}