5

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

Boris K
  • 1,469
  • 9
  • 29
  • 3
    If the value stored in it is of type `[]map[string]int`, type assert `[]map[string]int` from it, not `[]interface{}`. If the value may be other slice types too, use a type switch. – icza Sep 24 '18 at 12:51
  • 1
    The compiler tells you: You cannot. You have a []map[string]int and this is totally different from a []interface{}. – Volker Sep 24 '18 at 12:51
  • @icza It can be an array of anything – Boris K Sep 24 '18 at 14:07

6 Answers6

9

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]
1

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)
    }
}
anna
  • 187
  • 1
  • 8
1

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
}
TruBlu
  • 441
  • 1
  • 4
  • 15
  • Hi @TruBlu, I don't code in Go anymore, but it looks like this code doesn't work: https://go.dev/play/p/Cc0CBmlf2ss – Boris K Dec 13 '22 at 16:09
  • Here is an example. https://go.dev/play/p/tsapSFGLSfj – TruBlu Dec 13 '22 at 17:33
  • This is not a correct answer, you created a []interface so you could put it in your function! Please check my playground link in the original question – Boris K Dec 14 '22 at 11:46
  • You can simply add ```[]map[string]int``` as a type switch case. – TruBlu Dec 14 '22 at 13:34
  • Oh come on, please read the question ! > My input is an interface{}, and I know it can be an array of any type. – Boris K Dec 15 '22 at 09:09
  • Right, but you also said that you don't want to use reflection, which is what the current answer is using. I'm suggesting this for that reason. You should be able to enumerate the expected types in your code base. – TruBlu Dec 15 '22 at 13:45
0

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.

Kenny Grant
  • 9,360
  • 2
  • 33
  • 47
0

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
}
MGM
  • 1
-1

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

Eddy Hernandez
  • 5,150
  • 1
  • 24
  • 31
  • 1
    I don't know the exact type of testStruct, only that it is an array of something, which I why I need a `[]interface{}` – Boris K Sep 24 '18 at 14:06
  • 1
    @BorisK You need to know what kind of types you can receive, for example: https://tour.golang.org/methods/16 let me if this helps you, and then I can change my response – Eddy Hernandez Sep 24 '18 at 14:35