range over interface{} which stores a slice
Solution 1
Well I used reflect.ValueOf
and then if it is a slice you can call Len()
and Index()
on the value to get the len
of the slice and element at an index. I don't think you will be able to use the range operate to do this.
package main
import "fmt"
import "reflect"
func main() {
data := []string{"one","two","three"}
test(data)
moredata := []int{1,2,3}
test(moredata)
}
func test(t interface{}) {
switch reflect.TypeOf(t).Kind() {
case reflect.Slice:
s := reflect.ValueOf(t)
for i := 0; i < s.Len(); i++ {
fmt.Println(s.Index(i))
}
}
}
Go Playground Example: http://play.golang.org/p/gQhCTiwPAq
Solution 2
You don't need to use reflection if you know which types to expect. You can use a type switch, like this:
package main
import "fmt"
func main() {
loop([]string{"one", "two", "three"})
loop([]int{1, 2, 3})
}
func loop(t interface{}) {
switch t := t.(type) {
case []string:
for _, value := range t {
fmt.Println(value)
}
case []int:
for _, value := range t {
fmt.Println(value)
}
}
}
Check out the code on the playground.
Solution 3
there is one exception from the way interface{} behaves, @Jeremy Wall gave already pointer. if the passed data is defined as []interface{} initially.
package main
import (
"fmt"
)
type interfaceSliceType []interface{}
var interfaceAsSlice interfaceSliceType
func main() {
loop(append(interfaceAsSlice, 1, 2, 3))
loop(append(interfaceAsSlice, "1", "2", "3"))
// or
loop([]interface{}{[]string{"1"}, []string{"2"}, []string{"3"}})
fmt.Println("------------------")
// and of course one such slice can hold any type
loop(interfaceSliceType{"string", 999, map[int]string{3: "three"}})
}
func loop(slice []interface{}) {
for _, elem := range slice {
switch elemTyped := elem.(type) {
case int:
fmt.Println("int:", elemTyped)
case string:
fmt.Println("string:", elemTyped)
case []string:
fmt.Println("[]string:", elemTyped)
case interface{}:
fmt.Println("map:", elemTyped)
}
}
}
output:
int: 1
int: 2
int: 3
string: 1
string: 2
string: 3
[]string: [1]
[]string: [2]
[]string: [3]
------------------
string: string
int: 999
map: map[3:three]
Solution 4
Expanding on the answer provided by masebase, you could generalize the iteration on an interface{}
slice with a function like this:
func forEachValue(ifaceSlice interface{}, f func(i int, val interface{})) {
v := reflect.ValueOf(ifaceSlice)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() != reflect.Slice {
panic(fmt.Errorf("forEachValue: expected slice type, found %q", v.Kind().String()))
}
for i := 0; i < v.Len(); i++ {
val := v.Index(i).Interface()
f(i, val)
}
}
Then, you use it like this:
func main() {
data := []string{"one","two","three"}
test(data)
moredata := []int{1,2,3}
test(data)
}
func test(sliceIface interface{}) {
forEachValue(sliceIface, func(i int, value interface{}) {
fmt.Println(value)
}
}
Nucleon
R&D Developer at http://www.simpleviewinc.com/. Lead developer for http://www.visitlasvegas.com/. Personal programming blog at http://owenallen.me and on github at https://github.com/owenallenaz
Updated on July 13, 2022Comments
-
Nucleon almost 2 years
Given the scenario where you have a function which accepts
t interface{}
. If it is determined that thet
is a slice, how do Irange
over that slice?func main() { data := []string{"one","two","three"} test(data) moredata := []int{1,2,3} test(data) } func test(t interface{}) { switch reflect.TypeOf(t).Kind() { case reflect.Slice: // how do I iterate here? for _,value := range t { fmt.Println(value) } } }
Go Playground Example: http://play.golang.org/p/DNldAlNShB
-
Nucleon over 11 yearsWorks great, thank you. The only thing to add would be that
s.Index(i)
returns areflect.Value
so in my case I neededs.Index(i).Interface()
to reference the actual value. -
Ryan Walls almost 8 yearsWhat would you do if you had a pointer to a slice in
interface{}
? e.g.moredata := &[]int{1,2,3}
-
Ryan Walls almost 8 yearsAnswering my question: If you have a pointer to a slice instead of a slice, you'll need to use
Elem()
to get the underlying value. e.g.reflect.TypeOf(reflect.ValueOf(t).Elem().Interface()).Kind()
-
Amandeep kaur over 6 yearswhat if I want to get value of interface field from slice of interface without any struct. I have tried this solution but it is limited to get interface reference only from slice not the actual values of fields.
-
Nicolas Garnier over 4 yearswoh many thanks, that trick saved me a lot of time (and headache!)
-
user1028741 over 4 yearsThis will not work over an array though, only over a slice
-
Inanc Gumus over 4 years@user1028741 No, the code works for every type including arrays + You can always take a slice from an array
array[:]
. -
user1028741 about 4 yearsBut the type of an array will be with the actual capacity of it. [3]int is not of the same type as [2]int or []int. Therefore, if the type of 't' is actually an array the relevant case will not apply.
-
user1028741 about 4 yearsWhat I mean is your example will not work on arrays. I changed it here: play.golang.org/p/DAsg_0aXz-r
-
user1028741 about 4 yearsYou can't easily make it work, if you don't know the type of your parameter in the first place. In order to use your trick (array[:]), you'll have to use reflection to decide that it is an array, then cast to array - then generate a slice of it. That's why I said it works on a slice, not on an array. Clearly with enough effort you can produce a slice out of the array...
-
cnst almost 4 yearssad it doesn't work with
fallthough
, nor with specifying more than one value in acase
; otherwise, it'd be absolutely perfect. play.golang.org/p/swjPprq8KRi -
Inanc Gumus almost 4 years@cnst That'd work in a weakly-typed lang but not in Go, yeah.
-
cnst almost 4 years@InancGumus but a fall-through wouldn't be weakly-typed at all; it's likely that the compiler just doesn't support it yet; a fall-through-like scenario can already be accomplished by defining a new interface and avoiding having to have the switch on type, but, in that case, there'll be no type-checking (outside of runtime) that the implementations still implement the same interface that you define locally; realistically, I see no good reason for the
./prog.go:16:3: cannot fallthrough in type switch
error for play.golang.org/p/swjPprq8KRi. -
Inanc Gumus almost 4 years@cnst In Go, you can't morph types without explicitly typing them. That's why fallthrough doesn't work (and shouldn't) in a type switch. It's all about the very strong type system and Go's explicitness that expects you to type everything clearly.