A Laurent A Laurent - 2 months ago 7
JSON Question

Filter structure with several parameters

I have an array of a struct and a maps with variable name and some filter values.
I want to filter my array with my maps.

Example Go Playground:

package main

import "fmt"

type cnts []cnt

type cnt struct {
ID int `json:"Id"`
Area string `json:"Area"`
State string `json:"State"`
City string `json:"City"`
}

func main() {
mycnts := cnts{
cnt{124, "Here", "South", "Home"},
cnt{125, "Here", "West", "Home"},
cnt{126, "", "South", "Home"},
cnt{127, "Here", "West", "NY"}}
// my maps with filter
mapFilter := map[string]string{"Area": "Here", "City": "Home"}
fmt.Println(mapFilter)

mycntsFilter := make(cnts, 0)
for _, val := range mycnts {
// I want to select only row where the map filter it's ok
mycntsFilter = append(mycntsFilter, val)
fmt.Println(val, mycntsFilter)
}
}


What is the best way to filter my data with dynamic filter (Represente here by a map of string)?

Answer

Using golang package reflect in this particular case will be the best.

reflect will get you the fields of the struct and you can iterate over them comparing the values with the corresponding filter value.

The example is specific to the struct that you provided, but you can easily modify it to apply to all structs, again using reflection.

Example: Go Playground

package main

import (
        "fmt"
        "reflect"
)

type cnts []cnt

type cnt struct {
        ID    int    `json:"Id"`
        Area  string `json:"Area"`
        State string `json:"State"`
        City  string `json:"City"`
}
// Filtering function
func filterItem(val *cnt, filter map[string]string) bool {
        item := reflect.ValueOf(val).Elem()
        itemType := item.Type()
        isValid := true
        // Iterate over the struct fileds
        for i := 0; i < item.NumField(); i++ {
                field := item.Field(i)
                filterValue, ok := filter[itemType.Field(i).Name]
                if ok {
                        // filter out  
                        if filterValue != field.Interface() {
                                isValid = false
                                break
                        }
                } 

        }

        return isValid
}

func main() {
        mycnts := cnts{
                cnt{124, "Here", "South", "Home"},
                cnt{125, "Here", "West", "Home"},
                cnt{126, "", "South", "Home"},
                cnt{127, "Here", "West", "NY"}}

        // my maps with filter
        mapFilter := map[string]string{"Area": "Here", "City": "Home"}
        fmt.Println(mapFilter)

        mycntsFilter := make(cnts, 0)
        for _, val := range mycnts {
               if filterItem(&val, mapFilter) {
                      mycntsFilter = append(mycntsFilter, val)
               }
        }
        fmt.Println(mycntsFilter)
}
Comments