In Go, how can I make a generic function with slices? - go

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.

Related

How to convert a slice of int to a slice of string

Well, I created a slice of int like this:
list_of_id := []string {1,2,3,4}
My code would do a check if a variable in my slice (list_of_id):
func contains(s [] int, input int) bool {
for _, v := range s {
if v == input {
return true
}
}
return false
}
func main() {
list_of_id := [] int {1,2,3,4}
fmt.Println(contains(list_of_id, 1))
}
I want to create a function with the flexibility that I could input 1 or "1" as well.
My intention is to create an if else condition in which the slice of int [] int {1,2,3,4} will be converted into a slice of string [] string {"1","2","3","4"} to check again.
Anddddd, I don't know how to do so. I tried to google it out but all I found is a solution to convert this [] int {1,2,3,4} to this "{1,2,3,4}"
import "strconv"
func contains(s [] int, input interface{}) bool {
switch i := input.(type) {
case int:
for _, v := range s {
if v == i {
return true
}
}
case string:
for _, v := range s {
if strconv.Itoa(v) == i {
return true
}
}
}
return false
}
https://play.golang.org/p/02J1f77n_aM
package main
import (
"fmt"
"strconv"
)
func contains(s []int, input interface{}) bool {
var c int
var err error
switch input.(type) {
case int:
c = input.(int)
case string:
tmp := input.(string)
c, err = strconv.Atoi(tmp)
}
if err != nil {
return false
}
for _, v := range s {
if v == c {
return true
}
}
return false
}
func main() {
list_of_id := []int{1, 2, 3, 4}
fmt.Println(contains(list_of_id, 3))
fmt.Println(contains(list_of_id, 5))
fmt.Println(contains(list_of_id, "1"))
fmt.Println(contains(list_of_id, "6"))
}
Here example

Is it possible to stub/mock a function call without adding an interface?

Working with a codebase that doesn't have any interfaces in front of its Database calls, I want to test application logic that uses these functions. Is there any way to do this? Here is a simple example
func GetNumbers() []int {
return []int{1, 2, 3, 4}
}
func ProcessNumbers() bool {
numbers := GetNumbers()
numbers = Operation1(numbers)
numbers = Operation2(numbers)
return Operation3(numbers)
}
If GetNumbers was a database call, is there anyway for me to stub the return type of this function, without adding a interface of on GetNumbers?
Use a function:
func ProcessNumbers(get func() []int) bool {
numbers := get()
numbers = Operation1(numbers)
numbers = Operation2(numbers)
return Operation3(numbers)
}
Call it like this:
ProcessNumbers(GetNumbers)
...
ProcessNumbers(mockGetNumbers)
You can stub out GetNumbers as shown below
var GetNumbers = func() []int {
return []int{1, 2, 3, 4}
}
func ProcessNumbers() bool {
numbers := GetNumbers()
numbers = Operation1(numbers)
numbers = Operation2(numbers)
return Operation3(numbers)
}
Test code
func TestProcessNumbers(t *testing.T) {
var tempGetNumbers = GetNumbers
GetNumbers = func() []int {
return []int{1}
}
defer GetNumbers = tempGetNumbers
ProcessNumbers()
...

Iterate through all the methods using range for loop

I have a struct with methods defined in it, i want to iterate through all the methods. Can i use interface {} or any other way to achieve this ?
type SomeStruct struct {
m_boolVals []bool
m_stringVals []string
m_intVals []int64
}
func (m *SomeStruct) GetBool() []bool {
return m.m_boolVals
}
func (m *SomeStruct) GetString() []string {
return m_stringVals
}
func (m *SomeStruct) GetInts() []int64 {
return m_intVals
}
Is there a way to achieve below code ? So basically only one of value would be present
fun SomeOtherFunc(ss *SomeStruct) []string {
var final_string []string
for _, handlerFunc := range(ss.GetBool, ss.GetString, ss.GetInts) {
generic_vals := handlerFunc()
if (len(generic_vals) > 0) {
for _, t_val := range(generic_vals) {
final_string = append(final_string , string(t_val))
}
break
}
}
return final_string
}
Here's an example how you can use reflection to iterate over the methods and call each, and convert their result to string:
func SomeOtherFunc(ss *SomeStruct) []string {
var result []string
v := reflect.ValueOf(ss)
for i := 0; i < v.NumMethod(); i++ {
for _, res := range v.Method(i).Call(nil) {
result = append(result, fmt.Sprint(res.Interface()))
}
}
return result
}
Testing it:
fmt.Println(SomeOtherFunc(&SomeStruct{
m_boolVals: []bool{true, false},
m_stringVals: []string{"one", "two"},
m_intVals: []int64{1, 2, 3},
}))
Which outputs (try it on the Go Playground):
[[true false] [1 2 3] [one two]]

How to create a slice of variable type in Go?

I have a function.
func doSome(v interface{}) {
}
If I pass by pointer a slice of struct into the function, the function must fill the slice.
type Color struct {
}
type Brush struct {
}
var c []Color
doSome(&c) // after с is array contains 3 elements type Color
var b []Brush
doSome(&b) // after b is array contains 3 elements type Brush
Maybe I need use reflection, but how?
func doSome(v interface{}) {
s := reflect.TypeOf(v).Elem()
slice := reflect.MakeSlice(s, 3, 3)
reflect.ValueOf(v).Elem().Set(slice)
}
typeswitch!!
package main
import "fmt"
func doSome(v interface{}) {
switch v := v.(type) {
case *[]Color:
*v = []Color{Color{0}, Color{128}, Color{255}}
case *[]Brush:
*v = []Brush{Brush{true}, Brush{true}, Brush{false}}
default:
panic("unsupported doSome input")
}
}
type Color struct {
r uint8
}
type Brush struct {
round bool
}
func main(){
var c []Color
doSome(&c) // after с is array contains 3 elements type Color
var b []Brush
doSome(&b) // after b is array contains 3 elements type Brush
fmt.Println(b)
fmt.Println(c)
}
Go has no generics. Your possibilities are:
Interface dispatching
type CanTraverse interface {
Get(int) interface{}
Len() int
}
type Colours []Colour
func (c Colours) Get(i int) interface{} {
return c[i]
}
func (c Colours) Len() int {
return len(c)
}
func doSome(v CanTraverse) {
for i := 0; i < v.Len; i++ {
fmt.Println(v.Get(i))
}
}
Type switch as #Plato suggested
func doSome(v interface{}) {
switch v := v.(type) {
case *[]Colour:
//Do something with colours
case *[]Brush:
//Do something with brushes
default:
panic("unsupported doSome input")
}
}
Reflection as fmt.Println() do. Reflection is very powerful but very expensive, code may be slow. Minimal example
func doSome(v interface{}) {
value := reflect.ValueOf(v)
if value.Kind() == reflect.Slice {
for i := 0; i < value.Len(); i++ {
element := value.Slice(i, i+1)
fmt.Println(element)
}
} else {
fmt.Println("It's not a slice")
}
}

Contains method for a slice

Is there anything similar to a slice.contains(object) method in Go without having to do a search through each element in a slice?
Mostafa has already pointed out that such a method is trivial to write, and mkb gave you a hint to use the binary search from the sort package. But if you are going to do a lot of such contains checks, you might also consider using a map instead.
It's trivial to check if a specific map key exists by using the value, ok := yourmap[key] idiom. Since you aren't interested in the value, you might also create a map[string]struct{} for example. Using an empty struct{} here has the advantage that it doesn't require any additional space and Go's internal map type is optimized for that kind of values. Therefore, map[string] struct{} is a popular choice for sets in the Go world.
No, such method does not exist, but is trivial to write:
func contains(s []int, e int) bool {
for _, a := range s {
if a == e {
return true
}
}
return false
}
You can use a map if that lookup is an important part of your code, but maps have cost too.
Starting with Go 1.18, you can use the slices package – specifically the generic Contains function:
https://pkg.go.dev/golang.org/x/exp/slices#Contains.
go get golang.org/x/exp/slices
import "golang.org/x/exp/slices"
things := []string{"foo", "bar", "baz"}
slices.Contains(things, "foo") // true
Note that since this is outside the stdlib as an experimental package, it is not bound to the Go 1 Compatibility Promise™ and may change before being formally added to the stdlib.
With Go 1.18+ we could use generics.
func Contains[T comparable](s []T, e T) bool {
for _, v := range s {
if v == e {
return true
}
}
return false
}
The sort package provides the building blocks if your slice is sorted or you are willing to sort it.
input := []string{"bird", "apple", "ocean", "fork", "anchor"}
sort.Strings(input)
fmt.Println(contains(input, "apple")) // true
fmt.Println(contains(input, "grow")) // false
...
func contains(s []string, searchterm string) bool {
i := sort.SearchStrings(s, searchterm)
return i < len(s) && s[i] == searchterm
}
SearchString promises to return the index to insert x if x is not present (it could be len(a)), so a check of that reveals whether the string is contained the sorted slice.
Instead of using a slice, map may be a better solution.
simple example:
package main
import "fmt"
func contains(slice []string, item string) bool {
set := make(map[string]struct{}, len(slice))
for _, s := range slice {
set[s] = struct{}{}
}
_, ok := set[item]
return ok
}
func main() {
s := []string{"a", "b"}
s1 := "a"
fmt.Println(contains(s, s1))
}
http://play.golang.org/p/CEG6cu4JTf
If the slice is sorted, there is a binary search implemented in the sort package.
func Contain(target interface{}, list interface{}) (bool, int) {
if reflect.TypeOf(list).Kind() == reflect.Slice || reflect.TypeOf(list).Kind() == reflect.Array {
listvalue := reflect.ValueOf(list)
for i := 0; i < listvalue.Len(); i++ {
if target == listvalue.Index(i).Interface() {
return true, i
}
}
}
if reflect.TypeOf(target).Kind() == reflect.String && reflect.TypeOf(list).Kind() == reflect.String {
return strings.Contains(list.(string), target.(string)), strings.Index(list.(string), target.(string))
}
return false, -1
}
I think map[x]bool is more useful than map[x]struct{}.
Indexing the map for an item that isn't present will return false. so instead of _, ok := m[X], you can just say m[X].
This makes it easy to nest inclusion tests in expressions.
You can use the reflect package to iterate over an interface whose concrete type is a slice:
func HasElem(s interface{}, elem interface{}) bool {
arrV := reflect.ValueOf(s)
if arrV.Kind() == reflect.Slice {
for i := 0; i < arrV.Len(); i++ {
// XXX - panics if slice element points to an unexported struct field
// see https://golang.org/pkg/reflect/#Value.Interface
if arrV.Index(i).Interface() == elem {
return true
}
}
}
return false
}
https://play.golang.org/p/jL5UD7yCNq
Not sure generics are needed here. You just need a contract for your desired behavior. Doing the following is no more than what you would have to do in other languages if you wanted your own objects to behave themselves in collections, by overriding Equals() and GetHashCode() for instance.
type Identifiable interface{
GetIdentity() string
}
func IsIdentical(this Identifiable, that Identifiable) bool{
return (&this == &that) || (this.GetIdentity() == that.GetIdentity())
}
func contains(s []Identifiable, e Identifiable) bool {
for _, a := range s {
if IsIdentical(a,e) {
return true
}
}
return false
}
If it is not feasable to use a map for finding items based on a key, you can consider the goderive tool. Goderive generates a type specific implementation of a contains method, making your code both readable and efficient.
Example;
type Foo struct {
Field1 string
Field2 int
}
func Test(m Foo) bool {
var allItems []Foo
return deriveContainsFoo(allItems, m)
}
To generate the deriveContainsFoo method:
Install goderive with go get -u github.com/awalterschulze/goderive
Run goderive ./... in your workspace folder
This method will be generated for deriveContains:
func deriveContainsFoo(list []Foo, item Foo) bool {
for _, v := range list {
if v == item {
return true
}
}
return false
}
Goderive has support for quite some other useful helper methods to apply a functional programming style in go.
The go style:
func Contains(n int, match func(i int) bool) bool {
for i := 0; i < n; i++ {
if match(i) {
return true
}
}
return false
}
s := []string{"a", "b", "c", "o"}
// test if s contains "o"
ok := Contains(len(s), func(i int) bool {
return s[i] == "o"
})
If you have a byte slice, you can use bytes package:
package main
import "bytes"
func contains(b []byte, sub byte) bool {
return bytes.Contains(b, []byte{sub})
}
func main() {
b := contains([]byte{10, 11, 12, 13, 14}, 13)
println(b)
}
Or suffixarray package:
package main
import "index/suffixarray"
func contains(b []byte, sub byte) bool {
return suffixarray.New(b).Lookup([]byte{sub}, 1) != nil
}
func main() {
b := contains([]byte{10, 11, 12, 13, 14}, 13)
println(b)
}
If you have an int slice, you can use intsets package:
package main
import "golang.org/x/tools/container/intsets"
func main() {
var s intsets.Sparse
for n := 10; n < 20; n++ {
s.Insert(n)
}
b := s.Has(16)
println(b)
}
https://golang.org/pkg/bytes
https://golang.org/pkg/index/suffixarray
https://pkg.go.dev/golang.org/x/tools/container/intsets
I created the following Contains function using reflect package.
This function can be used for various types like int32 or struct etc.
// Contains returns true if an element is present in a slice
func Contains(list interface{}, elem interface{}) bool {
listV := reflect.ValueOf(list)
if listV.Kind() == reflect.Slice {
for i := 0; i < listV.Len(); i++ {
item := listV.Index(i).Interface()
target := reflect.ValueOf(elem).Convert(reflect.TypeOf(item)).Interface()
if ok := reflect.DeepEqual(item, target); ok {
return true
}
}
}
return false
}
Usage of contains function is below
// slice of int32
containsInt32 := Contains([]int32{1, 2, 3, 4, 5}, 3)
fmt.Println("contains int32:", containsInt32)
// slice of float64
containsFloat64 := Contains([]float64{1.1, 2.2, 3.3, 4.4, 5.5}, 4.4)
fmt.Println("contains float64:", containsFloat64)
// slice of struct
type item struct {
ID string
Name string
}
list := []item{
item{
ID: "1",
Name: "test1",
},
item{
ID: "2",
Name: "test2",
},
item{
ID: "3",
Name: "test3",
},
}
target := item{
ID: "2",
Name: "test2",
}
containsStruct := Contains(list, target)
fmt.Println("contains struct:", containsStruct)
// Output:
// contains int32: true
// contains float64: true
// contains struct: true
Please see here for more details:
https://github.com/glassonion1/xgo/blob/main/contains.go
There are several packages that can help, but this one seems promising:
https://github.com/wesovilabs/koazee
var numbers = []int{1, 5, 4, 3, 2, 7, 1, 8, 2, 3}
contains, _ := stream.Contains(7)
fmt.Printf("stream.Contains(7): %v\n", contains)
It might be considered a bit 'hacky' but depending the size and contents of the slice, you can join the slice together and do a string search.
For example you have a slice containing single word values (e.g. "yes", "no", "maybe"). These results are appended to a slice. If you want to check if this slice contains any "maybe" results, you may use
exSlice := ["yes", "no", "yes", "maybe"]
if strings.Contains(strings.Join(exSlice, ","), "maybe") {
fmt.Println("We have a maybe!")
}
How suitable this is really depends on the size of the slice and length of its members. There may be performance or suitability issues for large slices or long values, but for smaller slices of finite size and simple values it is a valid one-liner to achieve the desired result.

Resources