-1

Example source:

// source multidimensional slice
var source = []interface{}{
    "value1",
    "value2",
    1234,
    1234.1234,
    []int{222, 333},
    []float32{444.444, 555.555},
    []interface{}{555, "value4", []int{777, 888}}
}

Target:

// target []string
var target = []string{
    "value1",
    "value2",
    "1234",
    "1234.1234",
    "222",
    "333",
    "444.444",
    "555.555",
    "555",
    "value4",
    "777",
    "888"
}

I wrote the conversion function. But it seems to me cumbersome and it does not cover all possible options. Could you tell me please can be there is more elegant decision?

// convert multidimensional slice []interface{} to slice []string
func convert(raw interface{}) (result []string) {
    switch raw.(type) {
    case []string:
        result = append(result, raw.([]string) ...)
    case []int:
        for _, elem := range raw.([]int) {
            result = append(result, convert(elem) ...)
        }
    case string, int, int8, int16, int32, int64, float32, float64, complex64, complex128:
        result = append(result, fmt.Sprintf("%v", raw))
    case []float32:
        for _, elem := range raw.([]float32) {
            result = append(result, convert(elem) ...)
        }
    case []rune:
        for _, elem := range raw.([]rune) {
            result = append(result, convert(elem) ...)
        }
    case []int8:
        for _, elem := range raw.([]int8) {
            result = append(result, convert(elem) ...)
        }
    case []int16:
        for _, elem := range raw.([]int16) {
            result = append(result, convert(elem) ...)
        }
    case []int64:
        for _, elem := range raw.([]int64) {
            result = append(result, convert(elem) ...)
        }
    case []float64:
        for _, elem := range raw.([]float64) {
            result = append(result, convert(elem) ...)
        }
    case []complex64:
        for _, elem := range raw.([]complex64) {
            result = append(result, convert(elem) ...)
        }
    case []complex128:
        for _, elem := range raw.([]complex128) {
            result = append(result, convert(elem) ...)
        }
    case []interface{}:
        for _, elem := range raw.([]interface{}) {
            result = append(result, convert(elem) ...)
        }
    case [][]interface{}:
        for _, elem := range raw.([][]interface{}) {
            result = append(result, convert(elem) ...)
        }
    default:
        fmt.Println(fmt.Sprintf("Unknown type %v", raw))
        os.Exit(1)
    }
    return result
}
Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
kradwhite
  • 11
  • 2
  • "Could you tell me please can be there is more elegant decision?" No, there is no really better solutions. Maybe you could reduce linecount with reflection but this opens up a different can of worms. – Volker Jan 22 '19 at 04:40
  • Possible duplicate of [Type converting slices of interfaces in go](https://stackoverflow.com/questions/12753805/type-converting-slices-of-interfaces-in-go) – Jonathan Hall Jan 22 '19 at 06:33

1 Answers1

3

Use the reflect package to handle all types of slices in a few lines of code:

func convert(dst []string, v reflect.Value) []string {
    // Drill down to the concrete value
    for v.Kind() == reflect.Interface {
        v = v.Elem()
    }

    if v.Kind() == reflect.Slice {
        // Convert each element of the slice.
        for i := 0; i < v.Len(); i++ {
            dst = convert(dst, v.Index(i))
        }
    } else {
        // Convert value to string and append to result.
        dst = append(dst, fmt.Sprint(v.Interface()))
    }
    return dst
}

Call it like this:

stringSlice := convert(nil, reflect.ValueOf(source))

Run it on the Playground

Charlie Tumahai
  • 113,709
  • 12
  • 249
  • 242