How could I use a switch statement for this lesson?

The trouble is that the Switch statement is reading ‘0’ as ‘false’, because JavaScript is not statically typed. I don’t know how useful this is, but I managed to enforce the variable type as ‘number’ by using:

typeof age === 'number'

in the switch statement. I don’t know why this worked, but it seemed to force the browser to read the variable as a number. The full code I used is below, if anyone knows why this works please let me know.

let lifePhase = age => {
  switch(typeof age === 'number') {
    case (age >= 0 && age <= 3):
      return 'baby';
    case (age >= 4 && age <= 12):
      return 'child';
    case (age >= 13 && age <= 19):
      return 'teen';
    case (age >= 20 && age <= 64):
      return 'adult';
    case (age >= 65 && age <= 140):
      return 'senior citizen'
    default:
      return 'This is not a valid age';
  }
}

returns a boolean. Given that age is a number, the result will be true. So what you really have is,

switch (true) {

}

That gives a match for the relational expression in each case, which is also boolean. The first one that is true will be the branch that is followed.

1 Like

Thanks @mtf for letting me know. I had a feeling that the first condition of the switch statement was being combined with the other cases, so it would be similar to:

if (typeof age === 'number' && age >= 0 && age <=3) {
   // return 'baby'
}

I think it’s also a useful way to mitigate any erroneous data types like strings or chars, but that would mean I would need a conditional to catch them if they didn’t make it through the switch.

You’re welcome, @craig_watt .

Validation of inputs for this sort of logic would be ahead of the switch, to be sure. At this stage we are only directing traffic and would expect the inputs to be valid.

But, we still have default as an exit strategy.

The input would be validated before this point, not as an additional condition.

If we want number input in a function then we either validate it before calling the function or we delegate that added responsibility to the function. In a function we trap invalid inputs by trying them out with a function that we know will fail if the input is invalid… If the real inputs fail, we reject them in the kindest of manner.

I don’t understand why validating the input in a switch condition, that expects a variable to be “True” can be considered bad practice. If the validation were to be inside a function, for e.g.

const isNumeric = value => value === 'number'
switch (isNumeric(age)) {
}

It just creates more code, for the sake of isolation. The trouble that i have in understanding “Good Practice” is if it works to achieve a single purpose, which in this case is to have the value ‘0’ interpreted as a number not as a boolean, then I don’t see much of a problem.

The downside to doing validation prior to the switch statement for e.g.

let lifePhase = age => {
    if (age === 'number') {
        // The switch statement will still interpret this as "falsey"
        switch(age) {
          // ...
        }
    }
}

So, how would you recommend interpreting ‘0’ as a numeric value in a switch statement? I partly understand your reasoning for validation of data types, however I don’t understand why you wouldn’t want to use the method I stated to cast the ‘0’ or ‘1’ as a numeric value.

One could argue that you are missing the point. The above is not validating the input, but generating a boolean. The first case to match that will be the branch. The switch will still work.

We need to validate before the switch. Writing it like that in the expression argument is not a viable shortcut.

I think we’re coming at it from two different angles. I’m talking about an implicit form of ‘type-casting’ by having the switch confirm that the input “is numeric”, then it reads true for values “0” or “1” thus allowing subsequent cases to read the value as numeric. If the data was a string or a char, then the switch statement would not function.

If you’re looking to validate the input to ensure that the data is numeric prior to the switch statement and handling any erroneous data, with an error message for example, then that’s only part of the problem.

Alternatively, you can extract the “less than 0” case and the “is equal to 0” case, and provide a response to them individually:

if (age === 0) {
    return 'baby';
}

switch(age) {
   case age < 3:
     return 'baby';
}

The trouble is that will be repeating code, just so you can read in a ‘0’ as a numeric value and not a boolean. The only other way to do it is to use if/else statements the whole way, which is dreadful to read with more than three options.

(I try to avoid “else” statements wherever possible, because of the nesting situation that they can create).

Could you please provide an example of your approach for validating the value, so that I am able to understand your perspective? Thanks.

That much I get, but the switch isn’t confirming anything. The expression is just a boolean. All it sees is true or false.

if (isNaN(parseInt(age)) throw Error("TypeError")
switch (true) {

}

That is validation before the switch, which will be effective. The switch will only be reached if age is a number.

@mtf Thank you for clearing that up, I wasn’t sure to what level of detail you were talking about validation. I haven’t come to throwing Errors in JavaScript, only in Java, so I wasn’t aware of how much capacity there is for errors or exceptions in vanilla JS.

I often find throwing too many errors, when your logic can account for them can cause more problems down the line, because you always have to be mindful of what methods can throw them. I felt like this program was too small to throw an error of any value, because for me they should be used to break out of logic that would ultimately cause a system to break.

The switch itself would not be the tool to “confirm” anything outside of its own scope, but in using the expression to return a boolean value, as far as I understand the switch will not run if the boolean is ‘false’.

I also tend not to use “switch (true)” because it is not explicit in determining which value it is using, for e.g. in the case of multiple parameters.

I’ll keep in mind for the future that you can throw errors to break out of your code in JavaScript, but I will need to do some research in handling them beyond a try/catch block. It’s all about learning at the end of the day, I just thought I’d suggest something that no one else had, and wanted to find out why this might not have been considered.

1 Like

Hence, we use the method in the build process. It tells the programmer, uh, okay, we need some preliminary logic so this error cannot occur. Error trapping during the build stage is a critical debugging tool. Use it liberally, then with testing, mitigate the cause and remove the error trap. We want fatal errors while we build. They are sign posts along the journey that help us to get where we’re going.

The switch argument doesn’t have to be explicit, it’s an expression. A value is an expression. age refers to a value. true just as much so. This is what allows us to use relational expressions in the case parameters. All a switch is doing is matching the state expression to the required expression. No magic, really. There is no reason to not use this approach, other than some people are not comfortable with it.


Some silliness…

let n = -5;
let msg;
switch(n < 0 ? "negative" : n > 0 ? "positive": "zero") {
  case "negative": msg = "Pessimistic"; break
  case "positive": msg = "Optimistic"; break
  default: msg = "Neutral"
}
console.log(msg)    // Pessimistic

I would say that using switch(true) is case dependent for e.g. if you had multiple parameters / arguments within a method and you used:

const multipleParams => (age, day, month, year) => {
   switch(true) {
     case 22:
         console.log(`Your birthday is ${dob}, you are ${age} years old.`);
     break;
  }
}

Then that means you are unable to determine which value is being read by the switch statement, chances are is that it wouldn’t work.

Going back to the original issue for a moment with the switch statement. If I don’t use:

switch (typeof age === 'number) {
}

Then the switch statement reads “age = 0” as “false”, so it must be doing something other than setting the switch to “true”. So ultimately I don’t know what the problem is when using it - it might not be conventional but if it works, what’s the problem exactly?

I suppose the other way to resolve the issue would be to use strings instead, considering that “0” is not the same as 0.

The point of validation is to prevent the switch from being reached. As stated earlier, the above will not prevent the switch from working. All it will do is insert a boolean in its place. If that boolean is false then the first case that is falsy, will be the branch that is followed. This is faulty logic.

Why couldn’t you combine both options? The main reason for the boolean check in the switch statement is to ensure that the value “0” is read as a number. That’s what this method solves.

I don’t mean to be argumentative, I just passed the exercises using this method - so clearly it arrived at the correct evaluation. How is that faulty logic?

For the simple reason that it is not having the effect you hope for. The switch expression is not where validation is done. It is not a viable shortcut. Do the validation first, then run the switch on a valid value, or true if using relational expressions in the cases.

In my silliness example above we can see that the switch expression is evaluated and matched to the appropriate case. It’s crazy and not what we would expect to see in production code but it illustrates the nature of the switch and how it behaves. All a switch does is match expressions. Nothing more. No logic. No validation.

Neglected to address this. The case suggests the switch expression is a value, a number, not a boolean. Of course we would not use true as the switch expression, but a variable (hopefully valid).

The more you play with a switch the more you get to know it. Read nothing into it, though. The behavior we have described is not magic or novel. It’s moot.

We do have the default in which we can supply an appropriate action, yes, including validation countermeasure. The key is to try not to be too clever. This architecture is designed to be purposeful, not to circumvent good logic.