Skip to content

Instantly share code, notes, and snippets.

@kwilczynski
Created July 24, 2019 14:12
Show Gist options
  • Save kwilczynski/ca80d1bee25c95df170e097327511c23 to your computer and use it in GitHub Desktop.
Save kwilczynski/ca80d1bee25c95df170e097327511c23 to your computer and use it in GitHub Desktop.
Generic slice filter in Go.
package main
import (
"fmt"
"reflect"
"time"
)
func Filter(slice interface{}, filter interface{}) interface{} {
if slice == nil {
panic("first argument is not valid or nil pointer")
}
sliceKind := reflect.TypeOf(slice).Kind()
if sliceKind != reflect.Array && sliceKind != reflect.Slice {
panic("first argument is not a slice or array")
}
if filter == nil || reflect.TypeOf(filter).Kind() != reflect.Func {
panic("second argument is not a function or nil pointer")
}
filterValue := reflect.ValueOf(filter)
filterType := filterValue.Type()
filterArguments := filterType.NumIn() != 2 || filterType.NumOut() != 1
if filterArguments || filterType.Out(0).Kind() != reflect.Bool {
panic("second argument signature is not valid")
}
sliceValue := reflect.ValueOf(slice)
sliceType := sliceValue.Type()
if !sliceType.Elem().ConvertibleTo(filterType.In(0)) {
panic("second argument signature does not match first argument type")
}
resultsType := reflect.SliceOf(sliceType.Elem())
results := reflect.MakeSlice(resultsType, 0, 0)
for i := 0; i < sliceValue.Len(); i++ {
element := sliceValue.Index(i)
filterResult := filterValue.Call([]reflect.Value{element, reflect.ValueOf(i)})[0]
if filterResult.Interface().(bool) {
results = reflect.Append(results, element)
}
}
return results.Interface()
}
type Person struct {
Name string
DateOfBirth time.Time
}
func main() {
var t time.Time
var p []*Person
const dateFormat = `2006-01-02`
t, _ = time.Parse(dateFormat, `1879-03-14`)
p = append(p, &Person{"Albert Einstein", t})
t, _ = time.Parse(dateFormat, `1856-07-10`)
p = append(p, &Person{"Nikola Tesla", t})
t, _ = time.Parse(dateFormat, `1887-08-12`)
p = append(p, &Person{"Erwin Schrodinger", t})
persons := Filter(p, func(p *Person, _ int) bool {
t, _ := time.Parse(dateFormat, `1880-01-01`)
return p.DateOfBirth.After(t)
})
fmt.Printf("%T\n", persons)
for _, p := range persons.([]*Person) {
fmt.Printf("%+v\n", &Person{p.Name, p.DateOfBirth})
}
strings := Filter([]string{"a", "b", "c"}, func(s string, _ int) bool {
return s > "a"
})
fmt.Printf("%T\n", strings)
fmt.Println(strings)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment