I'm trying to find the best way for searching inside an Stuct array for getting a specific item with the id of the element.
type Device struct {
Addr net.Addr
AssignedId int
Data string
}
type RegistredDevices struct {
AllDevices []Device
}
Right now i do that
var currentDevice models.Device
for _, device := range registredDevices.AllDevices {
if device.AssignedId == id{
currentDevice = device
}
}
I expected to do something better like that for searching, but i don't know what do to if item can't be find. What should be the return ? Can i return nil or i need to return an empty Device ?
func (registerDevice *RegistredDevices) GetById(id int) Device{
for _, device := range registerDevice.AllDevices {
if device.AssignedId == id{
return device
}
else{
return ?????
}
}
}
var currentDevice = registredDevices.GetById(1)
To signal that an item wasn't found you could either return a pointer to an item (which would be nil if not found), or use two return values with an error or a boolean.
For example map lookups return a value, ok pair. Example from the spec:
An index expression on a map a of type map[K]V used in an assignment or initialization of the special form
v, ok = a[x]
v, ok := a[x]
var v, ok = a[x]
yields an additional untyped boolean value. The value of ok is true if the key x is present in the map, and false otherwise.
In your case it would be:
func (registerDevice *RegistredDevices) GetById(id int) (Device, bool) {
for _, device := range registerDevice.AllDevices {
if device.AssignedId == id {
return device, true
}
}
return Device{}, false
}
And then:
if currentDevice, ok := registredDevices.GetById(1); ok {
// found. use currentDevice
} else {
// not found
}
Related
I'm working on a resolver function for a GraphQL query for a BE I'm writing in Go. In the resolver, I have user data that I want to update, using an input value containing several possible update properties.
In JavaScript, this can be done quickly through destructuring (pseudo):
const mergedObj = {...oldProps, ...newProps}
For now, my resolver function looks like this (using gqlgen for GraphQL Go resolvers):
func (r *mutationResolver) ModifyUser(ctx context.Context, input *model.ModifyUserInput) (*model.User, error) {
id := input.ID
us, ok := r.Resolver.UserStore[id]
if !ok {
return nil, fmt.Errorf("not found")
}
if input.FirstName != nil {
us.FirstName = *input.FirstName
}
if input.LastName != nil {
us.LastName = *input.LastName
}
if input.ProfileImage != nil {
us.ProfileImage = input.ProfileImage
}
if input.Password != nil {
us.Password = *input.Password
}
if input.Email != nil {
us.Email = *input.Email
}
if input.InTomorrow != nil {
us.InTomorrow = input.InTomorrow
}
if input.DefaultDaysIn != nil {
us.DefaultDaysIn = input.DefaultDaysIn
}
r.Resolver.UserStore[id] = us
return &us, nil
}
This feels quite boilerplatey. Would it make sense in this situation to iterate through struct keys? Or is there another pattern I'm missing?
Use a function to reduce the boilerplate:
func mergef[T any](a, b *T) {
if b != nil {
*a = *b
}
}
...
mergef(&us.FirstName, input.FirstName)
mergef(&us.LastName, input.LastName)
...
Use the reflect package to reduce more boilerplate:
// merge sets fields in struct pointed to by d to
// dereferenced fields in struct pointed to by s.
//
// Argument s must point to a struct with pointer type
// fields.
// Argument d must point to a struct with fields that
// correspond to the fields in s: there must be a field
// in d with the same name as a field in s; the type of
// the field in s must be a pointer to the type of the field
// in d.
func merge(d, s any) {
sv := reflect.ValueOf(s).Elem()
dv := reflect.ValueOf(d).Elem()
for i := 0; i < sv.NumField(); i++ {
sf := sv.Field(i)
if sf.IsNil() {
continue
}
df := dv.FieldByName(sv.Type().Field(i).Name)
df.Set(sf.Elem())
}
}
Employ the function like this:
merge(us, input)
I am trying to recursively parse a form data dictionary into an embedded struct.
It is kindof working, but eventually only one field gets set. I figured that the field keeps being overridden by the old struct, but need some help finding the solution.
Entrypoint of the function:
func FormDataToStruct(data map[string]string, s any) {
// Parse the form data into the struct
v := reflect.ValueOf(s)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() != reflect.Struct {
panic("not a struct")
}
formParse(data, v, s)
}
Set the values on the topmost struct:
func formParse(data map[string]string, v reflect.Value, s any) {
for key, value := range data {
if hasField(v, key) {
println("Found field: " + key)
var val, err = TransformValue(s, key, value)
if err != nil {
panic(err)
}
SetValue(s, key, val)
delete(data, key)
}
}
// Set values of inner structs
for key, value := range data {
keys := strings.Split(key, "_")
// Parse the inner struct recursively with another function
recurseKeys(keys, value, v, s, s)
}
}
Struct looks like this:
type Me struct {
Name string
Age int
Friend struct {
Name string
Age int
}
}
Form data to parse looks like this:
map[name:John age:20 friend_name:Jane friend_age:20]
func recurseKeys(keys []string, value string, v reflect.Value, s any, parent any) {
if len(keys) == 1 {
// We are at the end of the keys
// Set the value
var val, err = TransformValue(s, keys[0], value)
if err != nil {
panic(err)
}
SetValue(s, keys[0], val)
return
}
// We are not at the end of the keys
// We need to iterate over the struct
for i := 0; i < v.NumField(); i++ {
if strings.EqualFold(v.Type().Field(i).Name, keys[0]) {
// We found the field
// Recurse with the next key
newS := reflect.New(v.Field(i).Type())
recurseKeys(keys[1:], value, v.Field(i), newS.Interface(), parent)
// Check if the field on the old struct is a pointer, if it is, we need to set the pointer
// If it is not a pointer, we need to set the value
if v.Field(i).Kind() == reflect.Ptr {
// Check if newS is a pointer
if newS.Kind() == reflect.Ptr {
v.Field(i).Set(newS)
} else {
v.Field(i).Set(newS.Elem())
}
} else {
// Check if newS is a pointer
if newS.Kind() == reflect.Ptr {
v.Field(i).Set(newS.Elem())
} else {
v.Field(i).Set(newS)
}
}
}
}
}
Running the above form data through the struct would result in the following output:
println(meInstance)
// {John 20 {Jane 0}}
Any help is very much appreciated!
EDIT
Go playground with minimal reproducible example:
https://go.dev/play/p/d5pIK3uQrUL
The issue with this code is that it creates a new struct and replaces it with the existing object. Therefore, you will always see the last assigned value.
The fixed version of the code will be like below:
func recurseKeys(keys []string, value string, v reflect.Value, s any, parent any) {
if len(keys) == 1 {
// We are at the end of the keys
// Set the value
var val, err = TransformValue(s, keys[0], value)
if err != nil {
panic(err)
}
SetValue(s, keys[0], val)
return
}
// We are not at the end of the keys
// We need to iterate over the struct
for i := 0; i < v.NumField(); i++ {
if strings.EqualFold(v.Type().Field(i).Name, keys[0]) {
// We found the field
// Recurse with the next key
if v.Field(i).IsZero() {
var newS reflect.Value
if v.Field(i).Kind() == reflect.Ptr {
newS = reflect.New(v.Field(i).Type().Elem()).Elem()
} else {
newS = reflect.New(v.Field(i).Type())
}
// Check if the field on the old struct is a pointer, if it is, we need to set the pointer
// If it is not a pointer, we need to set the value
if v.Field(i).Kind() == reflect.Ptr {
v.Field(i).Set(newS.Addr())
} else {
v.Field(i).Set(newS.Elem())
}
}
if v.Field(i).Kind() == reflect.Ptr {
recurseKeys(keys[1:], value, v.Field(i), v.Field(i).Interface(), parent)
} else {
recurseKeys(keys[1:], value, v.Field(i), v.Field(i).Addr().Interface(), parent)
}
}
}
}
This code accepts struct pointers as well.
Performance improvement tip: You may want to consider scanning the target object and creating a map of name -> Reflect value to reduce the number of loops.
Maintenance tip: It's better to consider using struct tags instead of reflecting the struct Variable name directly.
I'm trying to find a quick way of performing comparisons between two []reflect.Type. Right now I have the following:
func Equal(left, right []reflect.Type) bool {
if len(left) != len(right) {
return false
}
for i := 0; i < len(left); i++ {
if left[i] != right[i] {
return false
}
}
return true
}
Most of the slices don't change. So if I can find a way to hash them, I'd get a huge perf boost.
Background
I'm trying to (for fun) implement a form of function overloading in Go using the reflect package. The first thing I did is to convert each specialised/overloaded function into a signature type.
type Signature struct {
Variadic bool
In, Out []reflect.Type
}
The idea is that, when the overloaded function gets called, I'll convert the arguments into a slice of reflect.Type and then find a Signature where the In types match.
This works, but for each comparison, it's a linear scan which is pretty slow. If I could hash the slice of []reflect.Type I could stick that in a map and get constant time lookups.
I ended up abusing the built-in map to assign unique ids to each reflect.Type. Then I hash those using djb2.
type TypeCode struct {
seq int64
codes map[reflect.Type]int64
}
func (td *TypeCode) TypeID(t reflect.Type) int64 {
if code, ok := td.codes[t]; ok {
return code
}
td.seq++
td.codes[t] = td.seq
return td.seq
}
func (td *TypeCode) SliceTypeID(tt []reflect.Type) int64 {
id := int64(5381)
for _, t := range tt {
id = ((id << 5) + id) + td.TypeID(t)
}
return id
}
edit: I switched to a string based approach which is less efficient, but removes any potential for collisions.
type TypeCode struct {
seq int64
codes map[reflect.Type]string
}
func (td *TypeCode) TypeID(t reflect.Type) string {
if code, ok := td.codes[t]; ok {
return code
}
td.seq++
id := strconv.FormatInt(td.seq, 10)
td.codes[t] = id
return id
}
func (td *TypeCode) SliceTypeID(tt []reflect.Type) string {
ids := make([]string, len(tt))
for i := 0; i < len(tt); i++ {
ids[i] = td.TypeID(tt[i])
}
return strings.Join(ids, ".")
}
I am trying to make a 3 variable map in go so that you can do something like.
var postlist = make(map[int][int]bool)
postlist[postid][userid] = true
if postid[postid][userid] = true {
//do something
}
I have tried to make my own using a struct like
var postlist = make(map[int]cusmap)
type cusmap struct {
userid int
seen bool
}
but then I don't know how to check to check both the userid and seen bool condition.
I am not sure what you are trying to do, but a map is only a key/value. You can't have key/key/value. In order to do this, you need the value to be a map, so you would do:
http://play.golang.org/p/dOAXNAI4CO
package main
func main() {
var postlist = make(map[int]map[int]bool)
postid, userid := 0, 0
postlist[postid] = make(map[int]bool)
postlist[postid][userid] = true
if postlist[postid][userid] == true {
println("ok")
} else {
println("ko")
}
}
If you want to implement a set of int pairs, you can use a structure as the map key:
type postData struct {
userID int
postID int
}
Then make a map with postData keys and bool values:
postSet := map[postData]bool{
postData{1234, 7284}: true,
postData{7777, 1212}: true}
We can exploit the fact that if we give a non-existent index to the map, it will just return a zero-value.
For bool, the zero-value is false.
if postSet[postData{7777, 1212}] {
fmt.Println("post found.")
} else {
fmt.Println("no such post!")
}
Here's a full working example: http://play.golang.org/p/VJw9Vm8gHA
An alternative to #creack's approach is to define funcs on the map itself that way you don't have to manually check every time you want to set / unset something:
func main() {
cm := CustMap{}
pid, uid := 0, 0
cm.Set(pid, uid)
fmt.Println(cm.Exists(pid, uid))
fmt.Println(cm.Exists(pid, 10))
fmt.Println(cm.Exists(10, 10))
}
type CustMap map[int]map[int]struct{} //struct makes the map use 0 bytes for values instead of 1 bytes for bools, handy as the map grows
func (cm CustMap) Set(pid, uid int) {
if _, ok := cm[pid]; !ok {
cm[pid] = make(map[int]struct{})
}
cm[pid][uid] = struct{}{}
}
func (cm CustMap) Exists(pid, uid int) (ok bool) {
if u := cm[pid]; u != nil {
_, ok = u[uid]
}
return
}
playground
I know that Go doesn't support templates or overloaded functions, but I'm wondering if there's any way to do some kind of generic programming for variadic functions anyway?
I have many functions such as these:
func (this Document) GetString(name string, defaults ...string) string {
v, ok := this.GetValueFromDb(name)
if !ok {
if len(defaults) >= 1 {
return defaults[0]
} else {
return ""
}
}
return v.asString
}
func (this Document) GetInt(name string, defaults ...int) int {
v, ok := this.GetValueFromDb(name)
if !ok {
if len(defaults) >= 1 {
return defaults[0]
} else {
return 0
}
}
return v.asInt
}
// etc. for many different types
Is there any way to do this without having so much redundant code?
The most of what you can achieve is usage of interface{} type, something like this:
func (this Document) Get(name string, defaults ...interface{}) interface{} {
v, ok := this.GetValueFromDb(name)
if !ok {
if len(defaults) >= 1 {
return defaults[0]
} else {
return 0
}
}
return v
}
GetValueFromDb function should also be tweaked to return interface{} value and not some wrapper like now.
Then in the client code you can do the following:
value := document.Get("index", 1).(int) // Panics when the value is not int
or
value, ok := document.Get("index", 1).(int) // ok is false if the value is not int
This will yield some runtime overhead though. I'd better stick with separate functions and try to restructure the code somehow.
Here's a working example of how you could change your code.
package main
import (
"fmt"
)
type Document struct{
getSucceeds bool
}
func (d *Document) GetValueFromDb(name string) (interface{}, bool) {
return 1, d.getSucceeds
}
func (this Document) Get(name string, def ...int) interface{} {
v, ok := this.GetValueFromDb(name)
if !ok {
if len(def) >= 1 {
return def[0]
} else {
return 0
}
}
return v
}
func main() {
d1 := Document{true}
d2 := Document{false}
var int1, int2 int
int1 = d1.Get("foo", 2).(int)
int2 = d2.Get("foo", 2).(int)
fmt.Println(int1, int2)
}
Since you know what type you expect for the given name, you can write your Get method in a generic way, returning interface{}, and then assert the type at the call site. See the spec about type assertions.
There are different ways to emulate some aspects of generics in Go. There were lots of discussions on the mailing list. Often, there's a way to restructure code so it's less dependent on generics.
In the client code you can do like this :
res := GetValue("name", 1, 2, 3)
// or
// res := GetValue("name", "one", "two", "three")
if value, ok := res.(int); ok {
// process int return value
} else if value, ok := res.(string); ok {
// process string return value
}
// or
// res.(type) expression only work in switch statement
// and 'res' variable's type have to be interface type
switch value := res.(type) {
case int:
// process int return value
case string:
// process string return value
}