How to make multiple if-conditions efficiently in Go? - go

I have to check a set of variables, if they contain an empty string as value. If the string is empty, it should be set to a default value of "no value". What is a efficient way to handle this in Golang?
Example given:
func defaultValue (myObject) {
if myObject.name == "" {
myObject.name = "no value"
}
if myObject.color == "" {
myObject.color = "no value"
}
if myObject.typing == "" {
myObject.typing = "no value"
}
if myObject.price == "" {
myObject.price = "no value"
}
}
I know how to do it with simple if statements, but I am wondering if there is an approach more efficient to this.

You can create a separate function for that
func optional(input string) string {
if input == "" {
return "no value"
}
return input
}
func defaultValue (myObject) {
myObject.name = optional(myObject.name)
myObject.color = optional(myObject.color)
myObject.typing = optional(myObject.typing)
myObject.price = optional(myObject.price)
}

func aorb[T comparable](a, b T) T {
var z T
if a != z {
return a
}
return b
}
func defaultValue(o *myObject) {
o.name = aorb(o.name, "no value")
o.color = aorb(o.color, "no value")
o.typing = aorb(o.typing, "no value")
o.price = aorb(o.price, "no value")
}

You can create a generic helper that checks a specific field. If you pass the address of the field, the helper can also set the default value. Using variadic argument you can even list multiple fields (if they should have the same default value):
func check[T comparable](def T, fields ...*T) {
var zero T
for _, field := range fields {
if *field == zero {
*field = def
}
}
}
Testing it:
type Obj struct {
name, color, typing, price string
}
func main() {
o := Obj{color: "red"}
fmt.Printf("%#v\n", o)
check("no value", &o.name, &o.color, &o.typing, &o.price)
fmt.Printf("%#v\n", o)
}
This will output (try it on the Go Playground):
main.Obj{name:"", color:"red", typing:"", price:""}
main.Obj{name:"no value", color:"red", typing:"no value", price:"no value"}

Related

How to update slice inside map

I'm struggling with updating a slice inside map rulesByCountry without any success.
The value of enabled changes only inside the function UpdateName but the map itself still sees this value unchanged. I assume it's something to do with pointers. I guess I did not grasp the concept of it. Can someone direct me what I'm doing wrong here? I tried a lot of things and run out of options. I would appreciate any kind of help.
import (
"fmt"
)
// Consts
const RuleSeparateStreetNameFromHome string = "Separate street number from home"
// Types
type Address struct {
AddressLines []string
Country string
}
type RuleChain []RuleDefinition
type RuleDefinition struct {
Name string
Enabled bool
}
//Map
var rulesByCountry map[string][]RuleDefinition = map[string][]RuleDefinition{
"DE": {
{
Name: RuleSeparateStreetNameFromHome,
// TODO some logic,
Enabled: false,
},
},
}
func main() {
var addr *Address
addr = &Address{
AddressLines: []string{
"Street3",
},
Country: "DE",
}
rules := GetRulesForCountry(addr.GetCountry())
rules.UpdateName(RuleSeparateStreetNameFromHome)
fmt.Println(rules)
}
func GetRulesForCountry(country string) RuleChain {
if rules, ok := rulesByCountry[country]; ok {
return rules
}
return nil
}
func (a *Address) GetFirstAddressLine() string {
return a.GetAddressLine(1)
}
func (a *Address) GetAddressLine(lineNumber int) string {
if lineNumber <= 0 {
return ""
}
lines := a.GetAddressLines()
if len(lines) >= lineNumber {
return lines[lineNumber-1]
}
return ""
}
func (m *Address) GetAddressLines() []string {
if m != nil {
return m.AddressLines
}
return nil
}
func (r *RuleChain) UpdateName(name string) {
for _, rule := range *r {
if rule.Name == name {
rule.Enabled = true
fmt.Print(rule)
}
}
}
func (m *Address) GetCountry() string {
if m != nil {
return m.Country
}
return ""
}
Based on the inputs of mkopriva
package main
import (
"fmt"
)
// Consts
const RuleSeparateStreetNameFromHome string = "Separate street number from home"
// Types
type Address struct {
AddressLines []string
Country string
}
type RuleChain []*RuleDefinition
type RuleDefinition struct {
Name string
Enabled bool
}
//Map
var rulesByCountry map[string][]*RuleDefinition = map[string][]*RuleDefinition{
"DE": {
{
Name: RuleSeparateStreetNameFromHome,
// TODO some logic,
Enabled: false,
},
},
}
func main() {
var addr *Address
addr = &Address{
AddressLines: []string{
"Street3",
},
Country: "DE",
}
rules := GetRulesForCountry(addr.GetCountry())
rules.UpdateName(RuleSeparateStreetNameFromHome)
fmt.Println(rules[0])
}
func GetRulesForCountry(country string) RuleChain {
if rules, ok := rulesByCountry[country]; ok {
return rules
}
return nil
}
func (a *Address) GetFirstAddressLine() string {
return a.GetAddressLine(1)
}
func (a *Address) GetAddressLine(lineNumber int) string {
if lineNumber <= 0 {
return ""
}
lines := a.GetAddressLines()
if len(lines) >= lineNumber {
return lines[lineNumber-1]
}
return ""
}
func (m *Address) GetAddressLines() []string {
if m != nil {
return m.AddressLines
}
return nil
}
func (r *RuleChain) UpdateName(name string) {
for _, rule := range *r {
if rule.Name == name {
rule.Enabled = true
fmt.Print(rule)
}
}
}
func (m *Address) GetCountry() string {
if m != nil {
return m.Country
}
return ""
}
Output:
&{Separate street number from home true}&{Separate street number from home true}

Determine if type is a string using reflect

Some of the existing answers on here about how to determine the type of an object at runtime..god help us
if reflect.TypeOf(err) == string {
}
that doesn't compile
if reflect.TypeOf(err) == "string" {
}
neither does that or this:
if reflect.TypeOf(err).Kind() == "string" {
}
how do we do this?
If I use the typeof function given by one of the answers, I get:
Compare like string
if reflect.TypeOf(err).String() == "string" {
fmt.Println("hello")
}
Or using type assertions
type F = func()
func typeof(v interface{}) string {
switch v.(type) {
case int:
return "int"
case string:
return "string"
case F:
return "F"
//... etc
default:
return "unknown"
}
}
Then
var f F
if typeof(f) == "F"{
fmt.Println("hello F")
}
To compare types using reflect, compare reflect.Type values:
var stringType = reflect.TypeOf("") // this can be declared at package-level
if reflect.TypeOf(v) == stringType {
// v has type string
}
Given an arbitrary type name X, you can construct the type using:
var xType = reflect.TypeOf((*X)(nil)).Elem()
if reflect.TypeOf(v) == xType {
// v has type X
}
If you want to check to see if a value is some type, then use a type assertion:
if _, ok := v.(string); ok {
// v is a string
}
If you want to map types to strings, use a map keyed by reflect.Type:
var typeName = map[reflect.Type]string{
reflect.TypeOf((*int)(nil)).Elem(): "int",
reflect.TypeOf((*string)(nil)).Elem(): "string",
reflect.TypeOf((*F)(nil)).Elem(): "F",
}
...
if n, ok := typeName[reflect.TypeOf(f)]; ok {
fmt.Println(n)
} else {
fmt.Println("other")
}

How do I f.Type()=="string" directly using golang reflect?

Full code is here:
https://pastebin.com/xC1uQVBC
type UDD struct {
A string //content = "John"
B string //content = "Male"
C string //content = "12345678"
D int64 //content = ""
E uint64 //content = ""
Z string //content = "FIrst time user"
}
reflect_UDD := reflect.ValueOf(&UDD).Elem()
typeOf_UDD := reflect_UDD.Type()
for i := 0; i < reflect_UDD.NumField(); i++ {
f := reflect_UDD.Field(i)
if(f.Type()==reflect.TypeOf("string")){
//i would like to f.Type()=="string" directly...
//how is this possible and also for uint64 and int64 etc
}
}
Basically, I would like to do something along the lines of
f.Type()=="string"
or
f.Type()=="uint64"
or
f.Type()=="int64"
directly instead
Change f.Type() to f.Kind(), then use reflect Type to do judgement, supported types could be found https://godoc.org/reflect#Kind.
please see full example https://play.golang.org/p/Zydi7t3UBNJ
switch f.Kind() {
case reflect.Int64:
fmt.Println("got int64")
case reflect.String:
fmt.Println("got string")
case reflect.Uint64:
fmt.Println("got Unit64")
default:
fmt.Println("found nothing")
}
Declare variables for the types of interest. It's usually best to do this at package level.
var (
stringType = reflect.TypeOf("")
uint64Type = reflect.TypeOf(uint64(0))
... and so on
)
Compare to these types:
if f.Type() == stringType {
...
}
It's not possible to use f.Type()== "string" because strings are not assignable to reflect.Type values or vice versa.
Another option is to call Type.String(), but it's usually better to compare types than strings:
if f.Type().String == "string" {
...
}
f.Type().Kind().String()=="Uint64"
See reflect.Kind

Golang validator multifield dependency

I'd like to validate the following structure :
type CarModel struct {
gorm.Model
OwnerID int `json:"ownerid" validate:"nonzero"`
Type string `json:"type" validate:"regexp=(?)(A|B)"`
A string `json:"url" validate:"isurl"`
B string `json:"ip" validate:"isip"`
}
I would like to validate A and B depending on Type,
if type = A then A must exist and must be a URL BUT B must not exist
if type = B then A must not exist and B must be an IP
is this possible with validator ?
I did try custom validation but I cannot find a way to see type value :
func checkB(v interface{}, param string) error {
theB := reflect.ValueOf(v)
if theB.Kind() != reflect.String {
return validator.ErrUnsupported
}
//check if B is an IP
ipcool := net.ParseIP(theB.String())
if ipcool == nil {
return errors.New("B : ip incorrecte " + theB.String())
}
return nil
}
Upon the answer of Alex Nichol, I would like first to thank you for your help.
If I understood correctly, I would have to iterate through all the "validate" fields, to keep a trace of the value of TYPE, A and B and then to check them depending on TYPE ...
I did this :
func checkMonitor(v interface{}) error {
var mytype string
var myA string
var myB string
val := reflect.ValueOf(v)
// Iterate through fields
for i := 0; i < val.NumField(); i++ {
// Lookup the validate tag
field := val.Type().Field(i)
tags := field.Tag
_, ok := tags.Lookup("validate")
if !ok {
// No validate tag.
continue
}
// Get the value of the field.
fieldValue := val.Field(i)
switch field.Name {
case "Type":
mytype = fieldValue.Interface()
case "A":
myA = fieldValue.Interface()
case "B":
myB = fieldValue.Interface()
}
// Validation logic here.
//fmt.Println("field", field.Name, "has validate tag", validate, "and value", fieldValue.Interface())
}
if mytype == "A" {
if myA == "" {
return errors.New("A vide et type A")
}
ipcool := net.ParseIP(myA)
if ipcool == nil {
return errors.New("A incorrecte " + myA)
}
} else if mytype == "HTML" {
if myB == "" {
return errors.New("B vide et type B")
}
_, urlpascool := url.ParseRequestURI(myB)
if urlpascool != nil {
return errors.New("B incorrecte " + myB)
}
}
return nil
}
but got an error on the mytype, myA and myB in the switch case :
cannot use fieldValue.Interface() (type interface {}) as type string in assignment: need type assertion
EDIT :
just needed to use my brain :
switch field.Name {
case "Type":
mytype = fieldValue.String()
case "A":
myA = fieldValue.String()
case "B":
myB = fieldValue.Interface()
}
You probably want to use reflection to iterate over the fields of the struct, get the validate tag for each field, and check the field. This means you'll have to do validation on a struct level. Otherwise, if you pass something like myInstance.OwnerID to a function, you'll lose the tag associated with it.
This code loops through the fields of a struct and gets the validate tag for each:
func checkStruct(v interface{}) error {
val := reflect.ValueOf(v)
// Iterate through fields
for i := 0; i < val.NumField(); i++ {
// Lookup the validate tag
field := val.Type().Field(i)
tags := field.Tag
validate, ok := tags.Lookup("validate")
if !ok {
// No validate tag.
continue
}
// Get the value of the field.
fieldValue := val.Field(i)
// Validation logic here.
fmt.Println("field", field.Name, "has validate tag", validate, "and value",
fieldValue.Interface())
}
return nil
}
For example, we could pass it the following CarModel:
checkStruct(CarModel{
OwnerID: 2,
Type: "B",
A: "http://google.com",
B: "192.168.1.1",
})
and it would print out the following:
field OwnerID has validate tag nonzero and value 2
field Type has validate tag regexp=(?)(A|B) and value B
field A has validate tag isurl and value http://google.com
field B has validate tag isip and value 192.168.1.1
Seems like your validation rules are very complicated, but you can give validating a try.
Suppose we already have two customized validators IsURL and IsIP, then your validation rules can be implemented as follows:
import (
"regexp"
v "github.com/RussellLuo/validating"
)
// Customized validators
var (
MatchRegexp = func(pattern string) v.Validator {
return v.FromFunc(func(field v.Field) v.Errors {
switch t := field.ValuePtr.(type) {
case *string:
if matched, _ := regexp.MatchString(pattern, *t); !matched {
return v.NewErrors(field.Name, v.ErrInvalid, "does not match")
}
return nil
default:
return v.NewErrors(field.Name, v.ErrUnsupported, "is unsupported")
}
})
}
// IsURL and IsIP are omitted
)
type CarModel struct {
gorm.Model
OwnerID int `json:"ownerid"`
Type string `json:"type"`
A string `json:"url"`
B string `json:"ip"`
}
func main() {
car := CarModel{}
errs := v.Validate(v.Schema{
v.F("ownerid", &car.OwnerID): v.Nonzero(),
v.F("type", &car.Type): MatchRegexp("(A|B)"),
v.F("a", &car.A): v.Lazy(func() v.Validator {
if car.Type == "A" {
return v.All(v.Nonzero(), IsURL())
}
return v.Not(v.Nonzero())
}),
v.F("b", &car.B): v.Lazy(func() v.Validator {
if car.Type == "B" {
return v.All(v.Nonzero(), IsIP())
}
return v.Not(v.Nonzero())
}),
})
}

Beego validation accepts invalid data

I am trying to validate some forms using Beego validation, but it is not working at all: invalid data passes without errors.
This the relevant code, I don't know what is wrong. Can you point me at the mistake?
https://github.com/dionyself/golang-cms/blob/master/models/form.go
package models
import (
"github.com/astaxie/beego"
"github.com/astaxie/beego/validation"
)
type BaseForm struct {
Errors map[string]string
}
func (form *BaseForm) Validate() bool {
valid := validation.Validation{}
b, err := valid.Valid(form)
if err != nil {
beego.Error(err)
}
if !b {
for _, err := range valid.Errors {
form.Errors[err.Key] = err.Message
beego.Debug(err.Key, err.Message)
}
}
return b
}
type RegisterForm struct {
BaseForm
Username string `form:"username" valid:"Required; AlphaNumeric; MinSize(4); MaxSize(300)"`
Password string `form:"password" valid:"Required; MinSize(4); MaxSize(30)"`
PasswordRe string `form:"passwordre" valid:"Required; MinSize(4); MaxSize(30)"`
}
func (form *RegisterForm) Valid(v *validation.Validation) {
// Check if passwords of two times are same.
if form.Password != form.PasswordRe {
v.SetError("PasswordRe", "Passwords did not match")
return
}
}
type ArticleForm struct {
BaseForm
Id int `form:"-"`
Title string `form:"title" valid:"Required;MinSize(4);MaxSize(300)"`
Category int `form:"category"`
Content string `form:"content" valid:"Required; MinSize(50); MaxSize(2000)"`
TopicTags string `form:"topic-tags" valid:"MinSize(4); MaxSize(300)"`
TaggedUsers string `form:"tagged-users" valid:"MinSize(4); MaxSize(300)"`
AllowReviews bool `form:"allow-reviews" valid:"Required"`
AllowComments bool `form:"allow-comments" valid:"Required"`
Errors map[string]string
}
func (form *ArticleForm) Valid(v *validation.Validation) {
if form.Category >= 0 {
v.SetError("Category", "Invalid category")
return
}
}
Some documentation:
http://beego.me/docs/mvc/controller/validation.md
This is the code that parses the form:
func (this *ArticleController) Post() {
form := models.ArticleForm{}
Art := new(models.Article)
if err := this.ParseForm(&form); err != nil {
this.Abort("401")
} else {
db := this.GetDB()
if !form.Validate() {
this.Data["form"] = form
var cats []*models.Category
db.QueryTable("category").All(&cats)
this.Data["Categories"] = cats
this.ConfigPage("article-editor.html")
for key, msg := range form.Errors {
fmt.Println(key, msg)
}
} else {
db.Insert(Art)
this.Data["Article"] = Art
this.ConfigPage("article.html")
}
}
}
Note: FormData is always accepted (even an empty form), form.Validate() is always returning 'true'... 0 errors on logs.
It's because your struct has a data type with map[string]interface{} which accepts any data type and converting it into a string try to be specific in data type

Resources