I have to check if a value is withing a range (0..9, 10..19..up to 100) and return a value depending on the range. The cyclomatic complexity of my function is 12 and I need to lower it to at least 9.
I'm really at a loss here.
I wanted to use an associative array (to use like a Dictionary or a Hash table), but I don't think it works with ranges and I don't want to have an array explicitly declared with 100 entries!
$value = 23;
switch(true) {
case in_array($value, range(0,9)):
return -10;
break;
case in_array($value, range(10,19)):
return -7;
break;
case in_array($value, range(20,29)):
return -5;
break;
case in_array($value, range(30,39)):
return 3;
break;
case in_array($value, range(40,49)):
return 4;
break;
case in_array($value, range(50,59)):
return 5;
break;
case in_array($value, range(60,69)):
return 6;
break;
case in_array($value, range(70,79)):
return 7;
break;
case in_array($value, range(80,89)):
return 8;
break;
case in_array($value, range(90,99)):
return 9;
break;
case in_array($value, range(100,100)):
return 10;
break;
default:
return 0;
break;
}
Can someone help? Is there a simpler way to do that?
TIA
Since the steps are regulary each 10, you can perform an integer division by 10 and lookup the corresponding values in an array:
function theFunc(int $i)
{
return ($i<0 || $i>100) ? 0 : [-10, -7, -5, 3, 4, 5, 6, 7, 8, 9, 10][(int)($i/10)];
}
for($i = -1 ; $i <= 101 ; $i++)
var_dump([$i, theFunc($i)]);
You can just use integer division if the number is greater than 30. You also don't need break; if you are returning, as it will already stop code execution.
switch(true) {
case in_array($value, range(0,9)):
return -10;
case in_array($value, range(10,19)):
return -7;
case in_array($value, range(20,29)):
return -5;
case in_array($value, range(30,100)):
return intdiv($value, 10);
default:
return 0;
}
Related
I have this method:
private function convertStatusStringToIntZeroOrOne(string $status)
{
$status = strtolower($status);
switch ($status) {
case "off":
case "0":
case 0:
$int_status = 0;
break;
case "on":
case "1":
case 1:
$int_status = 1;
break;
default:
$int_status = 1;
break;
}
return $int_status;
}
The $status parameter, when is the string "On" (with O letter capitalize), return 0 (zero).
Of course, I need return as 1.
Thank you
As you had numeric 0 and 1 in the options of the switch it was using a numeric comparison - "on" to a number is 0 and so it matched against 0.
As you have the parameter as type string a number would be converted to a string, so remove the numeric comparisons...
function convertStatusStringToIntZeroOrOne(string $status)
{
$status = strtolower($status);
switch ($status) {
case "off":
case "0":
$int_status = 0;
break;
case "on":
case "1":
$int_status = 1;
break;
default:
$int_status = 1;
break;
}
return $int_status;
}
echo convertStatusStringToIntZeroOrOne("On");
Although you could reduce the function to...
function convertStatusStringToIntZeroOrOne(string $status)
{
$status = strtolower($status);
return ($status == "off" || $status == 0)?0:1;
}
I have this PHP function for changing color between 2 numbers:
function color_switch($number){
switch (true){
case $number == range(1 , 3):
$color = "progress-bar-danger";
break;
case $number == range(3 , 5):
$color = "progress-bar-warning";
break;
case $number == range(5 , 6):
$color = "progress-bar-default";
break;
case $number == range(6 , 8):
$color = "progress-bar-success";
break;
case $number == range(8, 10):
$color = "progress-bar-success";
break;
}
return $color;
}
But in action this function does not work for me. How should I fix this ?
You are comparing range() which is an array, and $number is integer, which is invalid,
Change your function something like,
function color_switch($number) {
switch ($number) { // switching the function argument
case $number <= 3 : // if less than three, execute case
$color = "progress-bar-danger";
break;
case $number <= 5 :
$color = "progress-bar-warning";
break;
case $number <= 6 :
$color = "progress-bar-default";
break;
case $number <= 8 :
$color = "progress-bar-success";
break;
case $number <= 10 :
$color = "progress-bar-success";
break;
}
return $color;
}
Your utilization of switch is incorrect and your utilization of range() is too.
Your parameter of switch should be the variable you evaluate.
Range() will return an array containing the range.
So the correct code is better :
function color_switch($number) {
switch ($number){
case in_array($number, range(1 , 3)):
$color = "progress-bar-danger";
break;
case in_array($number, range(3 , 5)):
$color = "progress-bar-warning";
break;
case in_array($number, range(5 , 6)):
$color = "progress-bar-default";
break;
case in_array($number, range(6 , 8)):
$color = "progress-bar-success";
break;
case in_array($number, range(8 , 10)):
$color = "progress-bar-success";
break;
}
return $color;
}
I want to write a function that will return next power of 2. So if input is 18 it will return 32 which is the next number greater than 18 in the series 2, 4, 8, 16, 32, 64
If input is 40 it will return 64.
Currently I am using the following function:
switch($number) {
case in_array($number, range(0, 2)):
return 2;
break;
case in_array($number, range(3, 4)):
return 4;
break;
case in_array($number, range(5, 8)):
return 8;
break;
case in_array($number, range(9, 16)):
return 16;
break;
case in_array($number, range(17, 32)):
return 32;
break;
}
Although the above works well but as you can see it is not a very elegant solution and has its limitation. Just wondering if there is a in-built PHP function or some better way of doing it.
Thanks.
Try this :
pow(2,ceil(log($number,2)))
A more effective one :
function next_pow($number)
{
if($number < 2) return 1;
for($i = 0 ; $number > 1 ; $i++)
{
$number = $number >> 1;
}
return 1<<($i+1);
}
The following code should work:
function next_power_of_two($num){
if(is_numeric($num)){
if($num > 1){
return pow(2, ceil(log($num, 2)));
}
else{
return 1;
}
}
return false;
}
$a = next_power_of_two(18); // 32
$b = next_power_of_two("377"); // 512
$c = next_power_of_two(-4); // 1
$d = next_power_of_two("water"); // false
This works well:
php > echo pow(2, (strlen(decbin(9))));
16
Convert the number to decimal, and raise 2 to the power of the number of chars.
I´m trying to do some program which would transfer hexa do binary. Problem is in changing of A,B,C,..,F to 10,11,12,...,15 so i can work with them as with numbers. I made this function:
function odstran_pismena($pole)
{
$dlzka = count($pole);
for ($i = 0; $i< $dlzka; $i++)
switch ($pole[$i])
{
case 0: break;
case 1: break;
case 2: break;
case 3: break;
case 4: break;
case 5: break;
case 6: break;
case 7: break;
case 8: break;
case 9: break;
case ("A" || "a"): $pole[$i] = 10;
break;
case ("B" || "b"): $pole[$i] = 11;
break;
case ("C" || "c"): $pole[$i] = 12;
break;
case ("D" || "d"): $pole[$i] = 13;
break;
case ("E" || "e"): $pole[$i] = 14;
break;
case ("F" || "f"): $pole[$i] = 15;
break;
default: $pole[$i] = "ERROR";
break;
}
return $pole;
}
First i made array from string, and now i want to change letters to numbers.
I´m testing it with this string: $test = "AbCdEf2345";
I was expecting result 10 11 12 13 14 15 2 3 4 5 but all i have is 10 10 10 10 10 10 2 3 4 5
Am I doing some mystake?(Of course I am, but where?)
("A" || "a") evaluates to boolean value 'true', so all a to f will get caught by the case ("A" || "a" ) and result in 10.
Without using the hexdec() and with minimal change to your code:
function odstran_pismena($pole)
{
$dlzka = count($pole);
for ($i = 0; $i< $dlzka; $i++)
switch ($pole[$i])
{
case 0: break;
case 1: break;
case 2: break;
case 3: break;
case 4: break;
case 5: break;
case 6: break;
case 7: break;
case 8: break;
case 9: break;
case "A":
case "a": $pole[$i] = 10;
break;
case "B":
case "b": $pole[$i] = 11;
break;
case "C":
case "c": $pole[$i] = 12;
break;
case "D":
case "d": $pole[$i] = 13;
break;
case "E":
case "e": $pole[$i] = 14;
break;
case "F":
case "f": $pole[$i] = 15;
break;
default: $pole[$i] = "ERROR";
break;
}
return $pole;
}
When you have specified case ("A" || "a"): $pole[$i] = 10; it evaluates to true. So all your chars matched against true returns true. for 'case' conditions avoid using expressions. Use the static values your comparing such as
case "A":
case "a":
$pole[$i] = 10;
break;
An easy way to help you with that would be to use a strtolower:
switch(strtolower($pole[$i])) {
case "a" :...
break;
case "b": ...
break;
}
or simply, as the first line of your function:
$pole = strtolower($pole);
This way you won't have to bother with upper/lower casing.
You should use hexdec() and replace you whole for loop with:
for ($i = 0; $i< $dlzka; $i++)
{
$pole[$i] = hexdec($pole[$i]);
}
Note that you will receive a 0 for non-valid values so you might have to check for that separately if it can happen, using for example is_numeric on the original value.
if (foo >= bar) baz();
But let's say sometimes baz(); needs to be run when foo <= bar, or foo == bar... and let's say that this comparison operator is grabbed from, say, a db table, and placed into a variable: $param = ">=".
Is there any way you could modify the first line to use $param, besides a switch-case with multiple if statements?
In my code, baz(); spans about a whole bunch of lines, and would become an organizational nightmare were I to manage it by hand.
function lt($a, $b)
{
return $a < $b;
}
...
$relops = Array(
'<' => 'lt',
...
);
echo $relops['<'](2, 3);
I solved this using:
function doComparison($a, $operator, $b)
{
switch ($operator) {
case '<': return ($a < $b); break;
case '<=': return ($a <= $b); break;
case '=': return ($a == $b); break; // SQL way
case '==': return ($a == $b); break;
case '!=': return ($a != $b); break;
case '>=': return ($a >= $b); break;
case '>': return ($a > $b); break;
}
throw new Exception("The {$operator} operator does not exists", 1);
}
Use eval()?
$param = ">=";
eval ("if (foo $param bar ) baz();");
Read more on the documentation page for the eval function.
EDIT:
Indeed, as others have mentioned, if there are alternatives, they are almost always better than eval(). If used, it must be used with care.
elaborating on my comment:
function variableOpComparison($v1, $v2, $o) {
if ($o == '!=') return ($v1 != $v2); // could put this elsewhere...
$operators = str_split($o);
foreach($operators as $operator) {
switch ($operator) { // return will exit switch, foreach loop, function
case '>': if ($v1 > $v2) return true; else continue; break;
case '<': if ($v1 < $v2) return true; else continue; break;
case '=': if ($v1 == $v2) return true; else continue; break;
default: throw new Exception('Unrecognized operator ' . $operator . ' in ' . $o);
}
}
return false;
}