Switch doesn't work with numeric comparison cases - php

Here is my code.
$value = 0;
switch($value) {
case ( $value <= 25 ):
$CompScore = 'low';
break;
case ($value > 25 && $value <= 50 ):
$CompScore = 'fair';
break;
case ($value > 50 && $value <= 75 ):
$CompScore = 'good';
break;
case ($value >75 ):
$CompScore = 'excellent';
break;
default:
$CompScore = 'low';
break;
}
echo $CompScore;
When the value is 0, $compScore is fair. Why it is not showing low? I don't understand why.

switch not working like that.
Since $value is 0 which is falsy value.
$value <= 25 is true, $value > 25 && $value <= 50 is false, so $CompScore will be 'fair'.
For you code, use an if elseif else flow will be more readable.
You could rewrite your code like below:
// from excellent to low
if ($value > 75) {
$CompScore = 'excellent';
} else if ($value > 50) {
$CompScore = 'good';
} else if ($value > 25) {
$CompScore = 'fair';
} else {
$CompScore = 'low';
}

The problem is, you use your switch in a particular way.
You are saying :
$value = 0;
Switch ($value){
case ($value < 25):
....
}
This finally compares $value<25 and 0 as described below :
($value<25) == $value.
=> true == 0
Which is wrong because true != 0
A way to do what you want this way is simply to replace switch($value) with switch(true) so the interpreter will actually compare check if your case statements are true.

The value you pass into a switch statement is basically what the switch statement looks for an evaluated match for, going from top to bottom down the list of cases until it finds one it is equal to (loose comparison), e.g. true == true.
In your example, your comparisons are evaluated as booleans (true or false) - your variable $value is set to zero, which is equal to false, but not identical (strict comparison) to false. For example:
(0 == false) // true
(0 === false) // false
(1 == false) // false
(1 === false) // false
(1 == true) // true
(1 === true) // false
(true === true) // true
So by using a boolean true as your switch value, you can do this to have numeric comparison inside the statement, where each comparison will evaluate to either true or false to match/not match the original true value (boolean - boolean comparison).
switch(true) {
case ($value <= 25): // true
$CompScore = 'low';
break;
case ($value > 25 && $value <= 50 ): // false
$CompScore = 'fair';
break;
case ($value > 50 && $value <= 75 ): // false
$CompScore = 'good';
break;
case ($value >75 ): // false
$CompScore = 'excellent';
break;
default: // if you removed the first case
$CompScore = 'low'; // this default case would be used
break;
}

You can "smarten up" your code with a lookup array and some simple mathematics.
Code: (Demo)
$lookup = [
1 => 'low',
2 => 'fair',
3 => 'good',
4 => 'excellent'
];
for ($i = -1; $i <= 101; ++$i) {
echo "\n$i : " , $lookup[ceil($i / 25)] ?? $lookup[1];
}
By dividing the value by 25, then rounding up to the next whole number (with ceil()), you achieve the same result without a battery of condition statements. Using a lookup away not only keeps your code compact, it ensures that you aren't performing n checks on the same value AND it provides a clean separation between the process and the values.
If you ever decide to split the groups by 20, instead of 25, you would only need to change the 25 to 20, then add a fifth lookup value in the desired location (with the appropriate key).

Related

PHP - Switch case evaluates to true while the same statement in if doesnt

I don't quite understands this.. Take a look at the following:
$value = 0;
if($value >= 90) {
// this does not return true
}
switch($value) {
case $value >= 90:
// this however does
break;
}
Am i missing something very obvious ?
$value >= 90 evaluates to false
As $value is 0, it is considered false. That's why your case works.
In a simple way it can be rewritten as:
switch($value) {
case false:
// this works
break;
}

php switch case statement to handle ranges

I'm parsing some text and calculating the weight based on some rules. All the characters have the same weight. This would make the switch statement really long can I use ranges in the case statement.
I saw one of the answers advocating associative arrays.
$weights = array(
[a-z][A-Z] => 10,
[0-9] => 100,
['+','-','/','*'] => 250
);
//there are more rules which have been left out for the sake of clarity and brevity
$total_weight = 0;
foreach ($text as $character)
{
$total_weight += $weight[$character];
}
echo $weight;
What is the best way to achieve something like this?
Is there something similar to the bash case statement in php?
Surely writing down each individual character in either the associative array or the switch statement can't be the most elegant solution or is it the only alternative?
Well, you can have ranges in switch statement like:
//just an example, though
$t = "2000";
switch (true) {
case ($t < "1000"):
alert("t is less than 1000");
break
case ($t < "1801"):
alert("t is less than 1801");
break
default:
alert("t is greater than 1800")
}
//OR
switch(true) {
case in_array($t, range(0,20)): //the range from range of 0-20
echo "1";
break;
case in_array($t, range(21,40)): //range of 21-40
echo "2";
break;
}
$str = 'This is a test 123 + 3';
$patterns = array (
'/[a-zA-Z]/' => 10,
'/[0-9]/' => 100,
'/[\+\-\/\*]/' => 250
);
$weight_total = 0;
foreach ($patterns as $pattern => $weight)
{
$weight_total += $weight * preg_match_all ($pattern, $str, $match);;
}
echo $weight_total;
*UPDATE: with default value *
foreach ($patterns as $pattern => $weight)
{
$match_found = preg_match_all ($pattern, $str, $match);
if ($match_found)
{
$weight_total += $weight * $match_found;
}
else
{
$weight_total += 5; // weight by default
}
}
You can specify the character range using regular expression. This saves from writing a really long switch case list. For example,
function find_weight($ch, $arr) {
foreach ($arr as $pat => $weight) {
if (preg_match($pat, $ch)) {
return $weight;
}
}
return 0;
}
$weights = array(
'/[a-zA-Z]/' => 10,
'/[0-9]/' => 100,
'/[+\\-\\/*]/' => 250
);
//there are more rules which have been left out for the sake of clarity and brevity
$total_weight = 0;
$text = 'a1-';
foreach (str_split($text) as $character)
{
$total_weight += find_weight($character, $weights);
}
echo $total_weight; //360
Much different ways to do this.
$var = 0;
$range_const = range(10,20);
switch ($var) {
case 1: $do = 5; break; # 1
case 2: $do = 10; break; # 2
case 3:
case 4:
case 5: $do = 15; break; # 3, 4, 5
default:
if ($var > 5 && $var < 10) { # High performance (6..9)
$do = 20;
} else if (in_array($var, $range_const, true)) { # Looks clear (10..20)
$do = 25;
} else { # NOT in range 1..20
$do = -1;
}
}
print($do);
There no direct range X..Y compares because $var checks to true in each step, but this allows do some nice cheating like this...
$in = create_function('$a,$l,$h', 'return $a>=$l && $a<=$h;');
$var = 4;
switch (true) {
case ($var === 1): echo 1; break;
case ($var === 2): echo 2; break;
case $in($var, 3, 5): echo "3..5"; break;
case $in($var, 6, 10): echo "6..10"; break;
default: echo "else";
}
If you have a more complex conditions, you can wrap them inside a function. Here's an oversimplified example:
$chartID = 20;
$somethingElse = true;
switch (switchRanges($chartID, $somethingElse)) {
case "do this":
echo "This is done";
break;
case "do that":
echo "that is done";
break;
default:
echo "do something different";
}
function switchRanges($chartID, $somethingElse = false)
{
if (in_array($chartID, [20, 30]) && $somethingElse === true) {
return "do this";
}
if (in_array($chartID, [20, 50]) && $somethingElse === false) {
return "do that";
}
}
I think I would do it in a simple way.
switch($t = 100){
case ($t > 99 && $t < 101):
doSomething();
break;
}

Case statement is not outputting correctly?

This is very strange but below is my case statement:
switch($grade){
case ($average >70):
$grade = 'A';
break;
case ($average >=60 && $average <=69):
$grade = 'B';
break;
case ($average >=50 && $average <=59):
$grade = 'C';
break;
};
So if its 70+ it is grade A, 60-69 grade B, 50-59 grade C.
But instead it outputting this: 60+ grade A, 50-59 grade B, 40-49 grade C.
Why is it doing this because function seems correct?
echo "<p><strong>Average Mark:</strong> $average</p>";
echo "<p><strong>Average Grade:</strong> $grade</p>";
As others mentioned in comments, the "condition" in a case should be a static value, not a logical expression.
Also, the value you're switching on (in your case, $grade) should is the one you're testing. You appear to be using it as a hint about what variable you're assigning.
The simplest way to fix your code would be to use an if-elseif-else construct:
if ($average >70)
$grade = 'A';
elseif ($average >=60 && $average <=69)
$grade = 'B';
elseif ($average >=50 && $average <=59)
$grade = 'C';
However, to be perverse, and to illustrate how a switch statement works, you could also do the following:
switch(true){
case ($average >70):
$grade = 'A';
break;
case ($average >=60 && $average <=69):
$grade = 'B';
break;
case ($average >=50 && $average <=59):
$grade = 'C';
break;
};
In this example I'm comparing the value true to each of the cases in turn, where each of those case-values is actually the result of evaluating a boolean expression. The first expression whose value matches true will fire.
Probably not much help, if you don't understand switch statements.
Edit: I just noticed that there's a gap in the logic: what if someone's average is exactly 70? Using a cascading statement like a switch or if-else, you can eliminate some of the redundant (and in this case damaging) code, thus:
if ($average >=70)
$grade = 'A';
elseif ($average >=60)
$grade = 'B';
elseif ($average >=50)
$grade = 'C';
// ...
else
$grade = 'F';
...and so on, to whatever lowest grade you're using.
For those of you saying switch the variable you $average, you are wrong. The only reason it is evaluating in that instance is because switch uses loose comparison, so it is saying that $average being set is true and comparing it to the conditionals, all of which will be either true or false. Previously, using $grade which was unset was evaluating the switch to false because in loose comparison, a variable which is unset will throw a notice and return false.
While I recommend using if-then-else, the proper answer for using a switch statement in this case is as follows:
switch (true) {
case ($average >= 70):
$grade = 'A';
break;
case ($average >= 60 && $average < 70):
$grade = 'B';
break;
case ($average >= 50 && $average < 60):
$grade = 'C';
break;
}
Like said above, every statement will return either true or false. The idea is that only one statement should ever return true at one time, thus the switch statement will match it's value of true to the one statement that passed and execute that code only, since all of the other ones are false and didn't match.
You should use if/else - statements:
if($average >70)
{
$grade = 'A';
} else if($average >=60 && $average <=69)
{
$grade = 'B';
} else if($average >=50 && $average <=59)
{
$grade = 'C';
}
Edit: Alternatively you can calculate the $grade value (50-100 in this example):
$grades = "CBAAA";
$grade = $grades[(int)($average/10) - 5];
The switch loop compares the cases. If you really want to use switch for the job (and not if / elseif / else), then you can to it with a switch that compares against TRUE:
switch(TRUE)
{
case $average > 70:
$grade = 'A';
break;
case $average >= 60 && $average <= 69:
$grade = 'B';
break;
case $average >= 50 && $average <= 59:
$grade = 'C';
break;
default:
throw new Exception(sprintf('Unable to map average (%d) to a grade.', $average));
}

switch statement will not equate zero?

I have the following function that works properly, except for when you use the input value of 0. I tried searching around to see if the 0 is equated as a NULL or if I'm doing something wrong.
When a zero is input, it outputs advanced which is greater than 20. Can anyone explain? Thanks
I plan on making the switch equate 0-10, 11-20, 21-30, 31-40, 41+ but for this example i am just using two scenarios. Thanks
**EDIT I do want values when its 20 :)
function strengthPlan( $data ) {
$data = ( int ) $data;
switch( $data ) {
case( $data <= 19 ):
$result = 'standard strength';
break;
case( $data >= 20 ):
$result = 'advanced strength';
break;
}
return $result;
}
echo strengthPlan( 0 );
Your logic is incorrect. Switch statements are checking for equality. Your code is checking whether $data is equal to TRUE or FALSE.
case( $data < 20 ):
will evaluate to:
case( TRUE ):
because 0 < 20.
Since 0 is not equal to TRUE but to FALSE (after conversion), the second case is run.
Basically, you cannot use switch case for < or > but only for ==.
That's not how switch statements. They compare the case to the value you provide to switch. Here's what you should do instead:
function strengthPlan($length) {
return $length >= 20 ? 'advanced strength' : 'standard strength';
}
If you're planning on using more conditions, then use an if...elseif statement as follows:
function strengthPlan($length) {
if ($length < 5) return 'micro strength';
elseif ($length < 10) return 'tiny strength';
elseif ($length < 15) return 'small strength';
elseif ($length < 20) return 'standard strength';
elseif ($length < 30) return 'advanced strength';
else return 'super strength!!!!!';
}
It will trickle down each condition until it hits a number that it is within. Alternatively, you can use sort of a lookup table style like this:
function strengthPlan($length) {
$plans = array(
1 => 'super strength!!!!!',
$length < 30 => 'advanced strength',
$length < 20 => 'standard strength',
$length < 15 => 'small strength',
$length < 10 => 'tiny strength',
$length < 5 => 'micro strength',
);
return $plans[1];
}
There was a discussion about this here: http://forums.devnetwork.net/viewtopic.php?f=50&t=113253
Using switch in this case is wrong
Use this one
http://sandbox.phpcode.eu/g/b4053.php
<?php
function strengthPlan( $data ) {
return ($data > 20) ? 'advanced strenght' : 'standard strenght';
}
echo strengthPlan( 0 );

strange beahviour with a php switch?

A sample of what I'm trying to do will be more explicit:
var_dump($opti_point); //int 0
if ($opti_point>=0 && $opti_point < 25) echo 'good';//echoing good
switch ($opti_point) {
case ($opti_point>= 0 && $opti_point < 25):
$test = 0;
break;
case ($opti_point >= 25 && $opti_point < 50):
echo 'we are in this case'; // This case is called !
$test = 2;
break;
default:
test = 0;
break;
}
Is there a trick here ?
thx
You cannot put comparisons inside "case" unfortunately...
A switch is only used when a value can have one of a limited number of values like so:
switch ( $val ) {
case 1:
echo "Got 1";
break;
case 2:
echo "Got 2";
break;
default:
echo "Got invalid value";
}
A workaround would be to use:
switch (true) {
case ($opti_point>= 0 && $opti_point < 25):
$test = 0;
break;
case ($opti_point >= 25 && $opti_point < 50):
echo 'we are in this case';
$test = 2;
break;
default:
test = 0;
break;
}
Which will work, but is a bit ugly...
Also, you're missing a single quote in echo we are in this case'; which should be echo 'we are in this case';
You should be using an if instead =)
You need to change the switch argument to true of false if you do comparison liek that in the cases.
You are not comparing what you think you are comparing. This is the code I think you want.
var_dump($opti_point); //int 0
if ($opti_point>=0 && $opti_point < 25) {
$test = 0;
echo 'You are now here!';
} elseif ($opti_point >= 25 && $opti_point < 50) {
$test = 2;
} else {
test = 0;
}
In your example, you are comparing the result of the logical statement...
($opti_point>=0 && $opti_point < 25) // true
To the value of $opti_point
0 // false
So PHP is actually converting what you think in an integer into a boolean to compare it with the result of the conditional statement.
I think that's a very bad way to use a switch statement, you should not put conditional sentences in the cases... In fact, I'm sure that that would be illegal in other languages and I'm not sure that it should work in PHP. Use a number of concatenated if-else conditions instead:
if ($i == 0) {
echo "i equals 0";
} elseif ($i == 1) {
echo "i equals 1";
} elseif ($i == 2) {
echo "i equals 2";
}
I'll go true the code with you
var_dump($opti_point); //int 0 , or false --- you should use TRUE
if ($opti_point>=0 && $opti_point < 25) echo 'good';//echoing good
switch ($opti_point) { // chose the case that is $opti_point (0 or false)
case ($opti_point>= 0 && $opti_point < 25): // true, so go to next
$test = 0;
break;
case ($opti_point >= 25 && $opti_point < 50): //false si this is the wan I pick
echo 'we are in this case'; // This case is called !
$test = 2;
break; // ingore the rest
default:
test = 0;
break;
}
you should use TRUE in the switch
if this is the exact code then try this
var_dump($opti_point); //int 0
if ($opti_point>=0 && $opti_point < 25) echo 'good';//echoing good
switch ($opti_point) {
case ($opti_point>= 0 && $opti_point < 25):
$test = 0;
break;
case ($opti_point >= 25 && $opti_point < 50):
echo 'we are in this case'; // This case is called !
$test = 2;
break;
default:
$test = 0;
break;
}
You have a misunderstanding on how switch-case works. Case DOES NOT TEST YOUR EXPRESSION TO BE boolean TRUE!
It compares its value to 'switch' value!
Here is an explanation:
$opti_point>= 0 && $opti_point < 25 evalutes to true which integer representation is 1 and since PHP can deal with types on it's own, it turnes true to 1 and compares it with value in switch which is 0
$opti_point >= 25 && $opti_point < 50 evaluates to false which is 0 as integer, so... that's your case ;)

Categories