Mar 10, 2024 · 15 mins read

Using Reflection in Go

Using Go's reflect package to filter based on selective fields on struct

Umakant Vashishtha


Using Reflection in Go

Introduction

Go is strongly typed language, which means that all types are known at compile time.

But sometimes we need to write code that can work with information that are not known at compile time.

For example, suppose we have a struct User as defined below:

type User struct { Name string Email string DateOfBirth int IsActive bool }

And we want to give the option to filter the users based on multiple fields like Name, Email, DateOfBirth, IsActive etc. For that we can define a function like this:

func FilterUsersUsingStruct(filter User) []User { var result []User for _, user := range users { if user.Name == filter.Name && user.Email == filter.Email && user.DateOfBirth == filter.DateOfBirth && user.IsActive == filter.IsActive { result = append(result, user) } } return result }

But there are few problems with this approach: It does not allow us to filter only on selective fields.

For example, we may want to filter only on Name and Email and ignore DateOfBirth and IsActive.

But with this approach, we have to provide all the fields in the filter struct, or if we don’t then some default values are assigned to the fields like 0 for DateOfBirth and false for IsActive as per go zero values.

So, to solve this problem, we can use Reflection in Go.

What is Reflection

Reflection is the ability of a program to examine its own structure, particularly through the types. It’s also the ability to manipulate the values of those types.

Reflection is a powerful feature that allows us to write more flexible and dynamic code.

The reflect package

The reflect package in Go provides the ability to inspect the type and value of a variable at runtime.

Using reflect, we can inspect the fields of a struct, the methods of a type, and the value of a variable.

This sounds promising for what we want to achieve. We can use reflect to filter based on selective fields.

Filter Argument with Selective Fields

We can define the filter argument to be a map of string to interface{} (map[string]interface{}) where the key is the field name and the value is the value to filter on.

var filterArgument = map[string]interface{}{ "Name": "John Doe", "Email": "john.doe@example.com", }

Using reflect with Structs

  • reflect.ValueOf

reflect.ValueOf takes any variable and returns reflect.Value which has some useful reflection related function on the passed object.

reflectUser := reflect.ValueOf(user)
  • reflect.Value.FieldByName

reflect.Value.FieldByName returns the struct field with the given name.

nameField := reflectUser.FieldByName("Name")
  • reflect.Value.Interface

reflect.Value.Interface returns the value of the reflect.Value as an interface{}.

nameValue := nameField.Interface()

We the above functions we can write a function to iterate over the key, values of the filter map and compare each field with the value of the struct.

Final Code

package users import ( "reflect" ) type User struct { Name string Email string DateOfBirth int IsActive bool } var users = []User{ { Name: "John Doe", Email: "john.doe@example.com", DateOfBirth: 1234567890, IsActive: true, }, { Name: "Jane Doe", Email: "jane.doe@example.com", DateOfBirth: 1234567890, IsActive: false, }, } func filterUserReflect(user User, filter map[string]interface{}) bool { reflectUser := reflect.ValueOf(user) for key, value := range filter { field := reflectUser.FieldByName(key) if !field.IsValid() { return false } entityValue := field.Interface() if entityValue != value { return false } } return true } func FilterUsers(filter map[string]interface{}) []User { var result []User for _, user := range users { if filterUserReflect(user, filter) { result = append(result, user) } } return result }

More on reflect package

The reflect package provides many more functions to inspect the type and value of a variable at runtime.

A lot of other libraries and frameworks use reflect package to provide dynamic features.

Some of the popular libraries that use reflect package are:

Caution

Using reflect package can be slow and error-prone. Use it only when necessary and don’t overuse it.

Using reflect package can make the code harder to read and understand.




Similar Articles

Home | © 2024 Last Updated: Mar 03, 2024
Buy Me A Coffee