Let's say I want to write a function that finds a value in a slice
I intuitively want to write:
func find(s []interface{}, f func(interface{})bool) int {
for i, item := range s {
if f(item) {
return i
}
}
return -1
}
however I don't manage to do this with Go. I could have an interface with
Len() int
Value(int) interface{}
...
and this would work but in my real code things are more complicated (I need to do slices[from:end] etc), append, ... etc and if I redefine all this in an interface I end up having a lot of code. Is there a better way?
You can use reflection. I wrote this function for a project, feel free to use it:
// InSlice returns true if value is in slice
func InSlice(value, slice interface{}) bool {
switch reflect.TypeOf(slice).Kind() {
case reflect.Slice, reflect.Ptr:
values := reflect.Indirect(reflect.ValueOf(slice))
if values.Len() == 0 {
return false
}
val := reflect.Indirect(reflect.ValueOf(value))
if val.Kind() != values.Index(0).Kind() {
return false
}
for i := 0; i < values.Len(); i++ {
if reflect.DeepEqual(values.Index(i).Interface(), val.Interface()) {
return true
}
}
}
return false
}
if you have predefined type like []int
or []string
and do not want to convert to []interface{}
see this working sample code (without using reflect
):
package main
import "fmt"
func find(s []int, f func(int) bool) int {
for i, item := range s {
if f(item) {
return i
}
}
return -1
}
func findString(s []string, f func(string) bool) int {
for i, item := range s {
if f(item) {
return i
}
}
return -1
}
func main() {
s := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Println(find(s, func(a int) bool { return a == 5 })) //5
strs := []string{"A", "B", "C"}
fmt.Println(findString(strs, func(a string) bool { return a == "B" })) //1
}
or you may use reflect
,like this working sample code:
package main
import "fmt"
import "reflect"
func find(slice interface{}, f func(interface{}) bool) int {
switch reflect.TypeOf(slice).Kind() {
case reflect.Slice:
values := reflect.Indirect(reflect.ValueOf(slice))
for i := 0; i < values.Len(); i++ {
if f(values.Index(i).Interface()) {
return i
}
}
}
return -1
}
func main() {
a := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Println(find(a, func(i interface{}) bool { return i == 5 })) //5
b := []string{"A", "B", "C"}
fmt.Println(find(b, func(i interface{}) bool { return i == "B" })) //1
}
output:
5
1
I hope this helps.
I think, if you want to have slice of arbitrary values and use that sort of find
function and have the possibility of standard []
reslicing, maybe the best way is to encapsulate your interface{}
with another struct
type proxy struct {
val interface{}
}
and use
func find(s []proxy , f func(proxy)bool) int {}
and have the f
function deal with interface{}
comparison / type casting.