5

How can I check if multiple strings are empty in an elegant way? This is how I currently do it:

//if one required field is empty, close the connection
    if (registerRequest.Email == "") ||
        (registerRequest.PhoneNumber == "")||
        (registerRequest.NachName =="") ||
        (registerRequest.VorName =="") ||
        (registerRequest.Password =="") ||
        (registerRequest.VerificationId ==""){

        //Could not proceed
        w.WriteHeader(UNABLE_TO_PROCEED)
        w.Write([]byte("Unable to register account."))
        return

    }
icza
  • 389,944
  • 63
  • 907
  • 827
thelearner
  • 1,440
  • 3
  • 27
  • 58
  • 4
    I'd also suggest using `http.StatusBadRequest` instead of a custom variable/constant `UNABLE_TO_PROCEED` -- 1. it's standard, so much more readable (anyone reading your code will know what it does), 2. your variable violates two Go idioms: Don't use `_` in variable names, and don't use all caps. – Jonathan Hall Jan 12 '18 at 13:02
  • 1
    An alternative way could be to add a metod for _registerRequest_ to check if any one of the mandatory fields is empty. – Mario Santini Jan 12 '18 at 13:04

3 Answers3

12

Note: You may use the solution below if you keep the "is-valid" condition in your handler, and also if you separate your condition into another function or method.

You can create a simple helper function, which has a variadic parameter, and you can call it with any number of string values:

func containsEmpty(ss ...string) bool {
    for _, s := range ss {
        if s == "" {
            return true
        }
    }
    return false
}

Example using it:

if containsEmpty("one", "two", "") {
    fmt.Println("One is empty!")
} else {
    fmt.Println("All is non-empty.")
}

if containsEmpty("one", "two", "three") {
    fmt.Println("One is empty!")
} else {
    fmt.Println("All is non-empty.")
}

Output of the above (try it on the Go Playground):

One is empty!
All is non-empty.

Your example would look like this:

if containsEmpty(registerRequest.Email,
    registerRequest.PhoneNumber,
    registerRequest.NachName,
    registerRequest.VorName,
    registerRequest.Password,
    registerRequest.VerificationId) {

    // One of the listed strings is empty
}

Also registerRequest is a kinda long name, it could be shortened to like r. If you can't or don't want to rename it in the surrounding code and if you want to shorten the condition, you could also do something like this:

If registerRequest is a pointer (or interface), you could also write:

if r := registerRequest; containsEmpty(r.Email,
    r.PhoneNumber,
    r.NachName,
    r.VorName,
    r.Password,
    r.VerificationId) {

    // One of the listed strings is empty
}

Actually you can do this even if registerRequest is not a pointer, but then the struct will be copied. If registerRequest is a struct, then you can take its address to avoid having to copy it like this:

if r := &registerRequest; containsEmpty(r.Email,
    r.PhoneNumber,
    r.NachName,
    r.VorName,
    r.Password,
    r.VerificationId) {

    // One of the listed strings is empty
}
icza
  • 389,944
  • 63
  • 907
  • 827
  • 1
    Terrible name hasZero, correct name should be isNotEmpty (https://talks.golang.org/2014/names.slide#2) and registerRequest should be r, but rename it in whole function do not make alias in if. – lofcek Jan 12 '18 at 14:50
  • @lofcek `isNotEmpty` would be a good name for a collection to check if its size is greater than 0. `hasZero()` tells if the list of passed values **has** a **zero** value (the value being equal to the zero value of its type). About `registerRequest`: I agree. – icza Jan 12 '18 at 15:14
  • @lofcek Although I agree that in case of `string` values, `containsEmpty()` would be more intuitive. Updated the answer. – icza Jan 12 '18 at 16:39
5

As Mario Santini mentioned in comment, a way to increase testability, encapsulate this logic, and decouple it from your handler method (which judging by the number of fields looks like it is at risk of changing at a different rate than your handler) could be to put this logic in a function:

func validRequest(registerRequest ?) bool {
   return registerRequest.Email == "" ||
        registerRequest.PhoneNumber == "" ||
        registerRequest.NachName == "" ||
        registerRequest.VorName == "" ||
        registerRequest.Password == "" ||
        registerRequest.VerificationId == ""
}

This now supports very focused, table driven tests, that can exercise what it means to be a valid request independent of any method involving writing headers.

It allows you to verify the valid/invalid path of your enclosing function, but to have very focused tests here. It also allows you to change what it means to be a valid request and verify it independent of your enclosing function.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
dm03514
  • 54,664
  • 18
  • 108
  • 145
1

You can use a switch:

switch "" {
case registerRequest.Email,
registerRequest.NachName,
registerRequest.Password,
registerRequest.PhoneNumber,
registerRequest.VerificationId,
registerRequest.VorName:
   w.WriteHeader(UNABLE_TO_PROCEED)
   w.Write([]byte("Unable to register account."))
   return
}

https://golang.org/ref/spec#Switch_statements

Zombo
  • 1
  • 62
  • 391
  • 407