Last active
May 13, 2016 19:29
-
-
Save flowonyx/2ff7535deba492a8eee37c760c140ac2 to your computer and use it in GitHub Desktop.
Filter a slice based on any field in the element using reflection
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import ( | |
"errors" | |
"reflect" | |
"github.com/fatih/structs" | |
) | |
// Filter takes a slice of structs that have a field with the given fieldName | |
// and uses that to filter based on value. The parameter, keep, determines | |
// whether the filter process keeps only the items filtered (true) or removes | |
// them (false). | |
func Filter(s interface{}, fieldName string, value interface{}, keep bool) error { | |
// ref is the pointer to the slice | |
ref := reflect.ValueOf(s) | |
// ensure that ref is a pointer to the slice | |
if ref.Kind() != reflect.Ptr || reflect.Indirect(ref).Kind() != reflect.Slice { | |
return errors.New("Filter must be called with a pointer to a slice") | |
} | |
// val is the slice | |
val := reflect.Indirect(ref) | |
// If the slice contains no items, no filtering is needed | |
if val.Len() == 0 { | |
return nil | |
} | |
// get the type of of item within the slice | |
typ := reflect.Indirect(ref).Type().Elem() | |
// check if this type has the field passed in | |
newElem := reflect.New(typ) | |
sin := structs.New(newElem.Interface()) | |
// If it does not have the field, nothing to filter on | |
_, ok := sin.FieldOk(field) | |
if !ok { | |
return errors.New("field not found") | |
} | |
// Create a new slice to hold the filtered results | |
results := reflect.MakeSlice(reflect.Indirect(ref).Type(), 0, 0) | |
// loop through the array and check each item | |
for i := 0; i < val.Len(); i++ { | |
v := val.Index(i) | |
sval := structs.New(v.Interface()) | |
b := sval.Field(field).Value() == value | |
if (keep && b) || (!keep && !b) { | |
results = reflect.Append(results, v) | |
} | |
} | |
// make the pointer go to the results slice | |
reflect.Indirect(ref).Set(results) | |
return nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment