patterngoModerate
Classifying by age
Viewed 0 times
classifyingagestackoverflow
Problem
Problem:
Classify the user (such as infant, child, teenager, adult) based on user's age.
Solution
Please run the solution here.
The code snippet written below works well as expected. I want to know if there's a better way to do or refine this.
Classify the user (such as infant, child, teenager, adult) based on user's age.
Solution
Please run the solution here.
The code snippet written below works well as expected. I want to know if there's a better way to do or refine this.
package main
import (
"errors"
"fmt"
)
func age(urAge int) (int, string, error) {
if urAge > 120 || urAge 0 && urAge 2 && urAge 12 && urAge <= 19 {
return urAge, "Teenager", nil
} else {
return urAge, "Adult", nil
}
}
func main() {
var input int
fmt.Println("Enter your age: ")
fmt.Scanf("%d", &input)
if a, b, e := age(input); e != nil {
fmt.Println("Error: ", e)
} else {
fmt.Println("Age: ", a)
fmt.Println("Classification: ", b)
}
}Solution
Note that your code classifies
Two completely different solutions will be presented:
1) Using
A much clearer way would be to use
If you write
Also I find it redundant to return 3 values: age, classification and an error. Since the returned age is identical to the input if no error is returned, it is unnecessary. Callers of your function will know not to use the age if an error is returned, so I would further simplify it like this:
And then of course usage slightly changes (slightly simplifies):
Alternative error reporting
Although in general it is a good pattern to return a value and an
And using it:
2) Using binary search
There is another elegant way. This is also faster if there are many cases. This solution is possible because age ranges of different classifications are disjunct (that is a concrete age can only belong to one classification). It also makes our life easier that age ranges of classifications are contiguous (we would need more ranges to model otherwise, but it wouldn't be a show-stopper).
The idea is that we list the age boundaries between classifications in an
Binary search in a sorted slice is implemented in the standard library, we can just use that:
Note that we will put 2 empty
Now let's see the implementation, it is rather short:
That's all! It's a one-line function!
Testing it:
Output (try it on the Go Playground):
```
Age: -100, Classification: Invalid age
Age: -1, Classification: Invalid age
Age: 0, Classification: Infant
Age: 2, Classification: Infant
Age: 3, Classification: Child
Age: 10, Classifi
age = 0 as "Adult" which you probably want to be "Infant". (This is because age = 0 is not included in any of your specified ranges and so the last else branch will be executed.)Two completely different solutions will be presented:
- The clearest and most obvious using
switch.
- An elegant, very compact and faster way using binary search.
1) Using
switchA much clearer way would be to use
switch.If you write
cases in increasing age order, you don't need to explicitly state both boundaries of a classification because cases are evaluated in order and the first one that matches will be executed:func age(urAge int) (int, string, error) {
switch {
case urAge 120:
return -1, "", errors.New("Invalid age")
case urAge <= 2:
return urAge, "Infant", nil
case urAge <= 12:
return urAge, "Child", nil
case urAge <= 19:
return urAge, "Teenager", nil
default:
return urAge, "Adult", nil
}
}Also I find it redundant to return 3 values: age, classification and an error. Since the returned age is identical to the input if no error is returned, it is unnecessary. Callers of your function will know not to use the age if an error is returned, so I would further simplify it like this:
func age(urAge int) (string, error) {
switch {
case urAge 120:
return "", errors.New("Invalid age")
case urAge <= 2:
return "Infant", nil
case urAge <= 12:
return "Child", nil
case urAge <= 19:
return "Teenager", nil
default:
return "Adult", nil
}
}And then of course usage slightly changes (slightly simplifies):
if b, e := age(input); e != nil {
fmt.Println("Error: ", e)
} else {
fmt.Println("Age: ", input)
fmt.Println("Classification: ", b)
}Alternative error reporting
Although in general it is a good pattern to return a value and an
error if a function can fail, in your case it doesn't really serve much purpose. It can only be "Invalid age". Also the empty string "" does not have a meaning as a classification, so you may want to exploit the empty string "" value to signal the error, and then you only need to return one value. If you choose to do this, the minimum is to include this in the documentation of the function like this:// age returns the age classification.
// Returns empty string "" if the age is invalid.
func age(urAge int) string {
switch {
case urAge 120:
return "" // Empty string means "Invalid age"
case urAge <= 2:
return "Infant"
case urAge <= 12:
return "Child"
case urAge <= 19:
return "Teenager"
default:
return "Adult"
}
}And using it:
if c := age(input); c == "" {
fmt.Println("Error: Invalid age")
} else {
fmt.Println("Age: ", input)
fmt.Println("Classification: ", c)
}2) Using binary search
There is another elegant way. This is also faster if there are many cases. This solution is possible because age ranges of different classifications are disjunct (that is a concrete age can only belong to one classification). It also makes our life easier that age ranges of classifications are contiguous (we would need more ranges to model otherwise, but it wouldn't be a show-stopper).
The idea is that we list the age boundaries between classifications in an
int slice in ascending order, and we can perform a binary search in this sorted slice. We can just model ranges with their maximum value since the sequence of ranges is without any holes. This will give us the index of the classification. We can store the classification names in a separate slice, and return the classification name at the index we just found as the result of binary search.Binary search in a sorted slice is implemented in the standard library, we can just use that:
sort.SearchInts().Note that we will put 2 empty
string names into the name slice (to the first and last place) which will be for the invalid ages (120).Now let's see the implementation, it is rather short:
var ages = []int{-1, 2, 12, 19, 120}
var classes = []string{"", "Infant", "Child", "Teenager", "Adult", ""}
// age returns the age classification.
// Returns empty string "" if the age is invalid.
func age(urAge int) string {
return classes[sort.SearchInts(ages, urAge)]
}That's all! It's a one-line function!
Testing it:
data := []int{-100, -1, 0, 2, 3, 10, 12, 13, 19, 20, 120, 121}
for _, d := range data {
if c := age(d); c == "" {
fmt.Printf("Age: %4d, Classification: Invalid age\n", d)
} else {
fmt.Printf("Age: %4d, Classification: %s\n", d, age(d))
}
}Output (try it on the Go Playground):
```
Age: -100, Classification: Invalid age
Age: -1, Classification: Invalid age
Age: 0, Classification: Infant
Age: 2, Classification: Infant
Age: 3, Classification: Child
Age: 10, Classifi
Code Snippets
func age(urAge int) (int, string, error) {
switch {
case urAge < 0 || urAge > 120:
return -1, "", errors.New("Invalid age")
case urAge <= 2:
return urAge, "Infant", nil
case urAge <= 12:
return urAge, "Child", nil
case urAge <= 19:
return urAge, "Teenager", nil
default:
return urAge, "Adult", nil
}
}func age(urAge int) (string, error) {
switch {
case urAge < 0 || urAge > 120:
return "", errors.New("Invalid age")
case urAge <= 2:
return "Infant", nil
case urAge <= 12:
return "Child", nil
case urAge <= 19:
return "Teenager", nil
default:
return "Adult", nil
}
}if b, e := age(input); e != nil {
fmt.Println("Error: ", e)
} else {
fmt.Println("Age: ", input)
fmt.Println("Classification: ", b)
}// age returns the age classification.
// Returns empty string "" if the age is invalid.
func age(urAge int) string {
switch {
case urAge < 0 || urAge > 120:
return "" // Empty string means "Invalid age"
case urAge <= 2:
return "Infant"
case urAge <= 12:
return "Child"
case urAge <= 19:
return "Teenager"
default:
return "Adult"
}
}if c := age(input); c == "" {
fmt.Println("Error: Invalid age")
} else {
fmt.Println("Age: ", input)
fmt.Println("Classification: ", c)
}Context
StackExchange Code Review Q#100690, answer score: 17
Revisions (0)
No revisions yet.