I have a large group of nested IF statements and I was wondering If anyone had any suggestions on how to optimize for speed, size, and readability.
Below is a sample of ONE of the if statements and its' nested statements. There will be approximately 25-30 of these in the document.
if( $row["inlet_moisture"] > $row["inlet_moisture_high_warning"] ) {
if( $row["inlet_moisture"] > $row["inlet_moisture_high_critical"] ) {
if( $row["inlet_high_critical"] == 0 ) {
if( $row["email_notification"] == 1 ) {
}
if( $row["mobile_notification"] == 1 ) {
}
}
} else {
if( $row["inlet_high_warning"] == 0 ) {
if( $row["email_notification"] == 1 ) {
}
if( $row["mobile_notification"] == 1 ) {
}
}
}
} else if( $row["inlet_moisture"] < $row["inlet_moisture_low_warning"] ) {
if( $row["inlet_moisture"] < $row["inlet_moisture_low_critical"] ) {
if( $row["inlet_low_critical"] == 0 ) {
if( $row["email_notification"] == 1 ) {
}
if( $row["mobile_notification"] == 1 ) {
}
}
} else {
if( $row["inlet_low_warning"] == 0 ) {
if( $row["email_notification"] == 1 ) {
}
if( $row["mobile_notification"] == 1 ) {
}
}
}
}
The idea is; I have a reading (temp/speed/moisture) and I need to check if it hits any of the limits (high warning / high critical / low warning / low critical), if it does I first need to check if I have already sent an alarm for that. If no alarm has been sent I then need to check if the user has requested alarm notification (mobile/email/both)
Currently this works. I Just don't like how heavy it is? Can I improve on this?
Thanks.
this seems to me much more clear, even though you could combine the nested ifs I'd rather prefer this one
if( $row["inlet_moisture"] > $row["inlet_moisture_high_critical"] ) {
if( $row["inlet_high_critical"] == 0 ) {
$message = 'the pertinent message';
}
}
else if( $row["inlet_moisture"] > $row["inlet_moisture_high_warning"] ) {
if( $row["inlet_high_warning"] == 0 ) {
$message = 'the pertinent message';
}
}
else if( $row["inlet_moisture"] < $row["inlet_moisture_low_critical"] ) {
if( $row["inlet_low_critical"] == 0 ) {
$message = 'the pertinent message';
}
}
else if( $row["inlet_moisture"] < $row["inlet_moisture_low_warning"] ) {
if( $row["inlet_low_warning"] == 0 ) {
$message = 'the pertinent message';
}
}
if( $row["email_notification"] == 1 ) {
sendMessage($message, $email);
}
if( $row["mobile_notification"] == 1 ) {
sendMessage($message, $mobile);
}
Premature optimisation is the root of all evil - with what we are dealing with here, no matter what you do it won't have much/any noticable impact on performance.
Having said that, a large amount of if statements can often be replaced with one or more switch structures, although whether this improves performance or readability is debatable. You may also be able to create some functions for repeated bits of code, although this may actually negatively impact performance.
From your comment above... creating variables with nicer names will have pretty much zero impact on performance. If will slightly increase your memory usage, but the processing time impact will be next to zero. And, if you are evaluating the values as booleans, you don't need to convert them to booleans explicitly, because 1 still evaluates to TRUE, and 0 to FALSE. However, if you do want to do it
$email_notification = $row["email_notification"] == 1 ? true : false;
...is unnecessarily long winded, you can do either this:
$email_notification = $row["email_notification"] == 1;
...or...
$email_notification = (bool) $row["email_notification"];
...and it will have the same effect.
Related
I have two (and sometimes) three variables which determine if I should run a specific code. The problem is that any of them can be true or false and sometimes this leads to messy codes.
For three variables, there are 8 possible scenarios that I have to check. Like this:
if($var_a && $var_b && $var_c) {
// Do this (A)
} else if(!$var_a && $var_b && $var_c) {
// Do this (B)
} else if($var_a && !$var_b && $var_c) {
// Do this (C)
} else if(!$var_a && !$var_b && $var_C) {
// Do this (D)
}
... and so on.
Is there any way to use nesting and make this code less messy? It gets confusing at times to keep track of so many possibilities.
It might be easier to understand what I am saying with an example of two variables.
if($var_a && $var_b) {
// Do this (A)
} else if($var_a && !$var_b) {
// Do this (B)
} else if(!$var_a && $var_b) {
// Do this (C)
}
Is there any way to combine these conditions together so that I don't have to use so many && and if else conditions? This will make things less confusing for me when I have to deal with three (may be more) variables.
I guess you should try something like lookup map with bitwise map, in your case, it can be something like this:
$bitOne = $var_a ? 1 : 0;
$bitTwo = $var_b ? 2 : 0;
$bitThree = $var_c ? 4 : 0;
$resultKey = $bitOne | $bitTwo | $bitThree;
$map = [
7 => function() { return 'All are true'; },
3 => function() { return 'var_a and var_b are true'; },
6 => function() { return 'var_b and var_c are true'; },
// and so on
];
$result = $map[$resultKey];
If you extract the common variable (or an arbitrary choice in this case), you can produce a more layered approach, although the combinations will still mount up and it looks more code, it should be clearer...
if($var_a) {
if(!$var_b) {
// $var_a && !$var_b
}
else {
// $var_a && $var_b
}
}
else {
if($var_b) {
// !$var_a && $var_b
}
}
if($_SESSION['valueofdie1'] != 0 && $_SESSION['valueofdie2'] != 0 && $_SESSION['valueofdie3'] != 0 && $_SESSION['valueofdie4'] != 0 && $_SESSION['valueofdie5'] != 0)
{
if((($_SESSION['valueofdie1'] == $_SESSION['valueofdie2']) && ($_SESSION['valueofdie2'] == $_SESSION['valueofdie3']||$_SESSION['valueofdie4']||$_SESSION['valueofdie5'])) || (($_SESSION['valueofdie1'] == $_SESSION['valueofdie3']) && ($_SESSION['valueofdie3'] == $_SESSION['valueofdie4']||$_SESSION['valueofdie5'])) || (($_SESSION['valueofdie1'] == $_SESSION['valueofdie4']) && ($_SESSION['valueofdie4'] == $_SESSION['valueofdie5']))
|| (($_SESSION['valueofdie2'] == $_SESSION['valueofdie3']) && ($_SESSION['valueofdie3'] == $_SESSION['valueofdie4']||$_SESSION['valueofdie5'])) || (($_SESSION['valueofdie2'] == $_SESSION['valueofdie4']) && ($_SESSION['valueofdie4'] == $_SESSION['valueofdie5']))
|| (($_SESSION['valueofdie3'] == $_SESSION['valueofdie4']) && ($_SESSION['valueofdie4'] == $_SESSION['valueofdie5'])))
{
if($_POST['choose'] == 'choose 3oaK')
{
$_SESSION['g'] = 5;
$_SESSION['scoretkind'] = $_SESSION['valueofdie1'] + $_SESSION['valueofdie2'] + $_SESSION['valueofdie3'] + $_SESSION['valueofdie4'] + $_SESSION['valueofdie5'];
unset($_SESSION['3oaKBut']);
echo '<input type="hidden" name="choose" value="Clear" onLoad="form.submit();">';
$_POST['sub'] = 'reset';
$_POST['choose'] = '';
}
if(empty($_SESSION['g']))
{
$_SESSION['3oaKBut'] = '<input type="submit" name="choose" value="choose 3oaK">';
echo $_SESSION['3oaKBut'];
}
}
}
if($_SESSION['g'] == 5)
{
echo $_SESSION['scoretkind'];
}
So here is the code we have. We are trying to check if 3 of the 5 die values are equal. If they are equal we echo out a button that allows the user to choose to score his 3 of a kind, which is the total of all of the dice. Everything works except in some cases the 3 of a kind button would echo out when there isnt a 3 of a kind. Halp PLS
I'm sorry I didn't answer your question by actually solving your bug, but I think your code is hard to read and your approach makes it cumbersome to program all the rules.
General advice: Put $_SESSION['valueofdie1'] and the other dice into an array of values. That's much easier to work with. After that, it should be pretty easy to check how many times each value occurs. Even when you keep your approach, you could make variables like $die1, which is already a lot shorter and more readable than $_SESSION['valueofdie1'].
But with an array, you could roughly start like this:
// Put all dice into an array.
$dice = array(
$_SESSION['valueofdie1'],
$_SESSION['valueofdie2'],
etc.... );
// Count how many times each die is rolled.
$diceCount = array();
foreach($dice as $die) {
$count = 0;
if (isset($diceCount[$die])) {
$count = $diceCount[$die];
}
$diceCount[$die] = $count + 1;
}
// Check possible results simply by looking at those counts.
// If one die value is rolled 5 times, it's Yahtzee...
if (array_search(5, $diceCount) !== false) {
echo 'Yahtzee!';
}
if (array_search(4, $diceCount) !== false) {
echo 'Four of a kind';
}
// Full house takes two types.
if (array_search(3, $diceCount) !== false && array_search(2, $diceCount) !== false) {
echo 'Full house';
}
for ($diceCount as $die => $count) {
echo "$count times $die";
}
... etc ...
You'll need to expand this list, and take some other rules into account. After all, a Yahtzee could also count as a Four of a Kind. But by checking all those rules, you can generate a new array of possible combinations, which you can check against the previously chosen options. And the outcome of that determines which options the player can choose.
Context: On our website, we calculate whether an item/order meets the criteria for free shipping using an if statement evaluating if a value - 'ff' - is true. Depending on conditions met it sets the shipping appropriately. We now require the need to evaluate if an item is free shipping with the order of _. To top it off, for other purposes such as external feeds, another condition must apply (weight must = 0 or have no value), for fw to be free freight. Here is the code, that I can't figure out why it is NOT working:
if($r['freeFreight'] == 'ff' || ($r['freeFreight'] == 'fw' && ($r['weight'] == 0 || $r['weight'] == '') ) ) {
$r['shippingStandard'] = 0;
}
Are the conditions overly segregated in the if statement with the sets of ()? Or, are they just incorrectly placed. For example, should it be:
if(($r['freeFreight'] == 'ff') || ($r['freeFreight'] == 'fw' && $r['weight'] == 0 || $r['weight'] == '') ) {
$r['shippingStandard'] = 0;
}
The second of the two seems more logical to me because: if ($r['freeFreight'] == 'ff') - then regardless of the following conditions the statement should return true and set the variable to 0. Is my logic correct? Sorry for this newb question...
Thanks for any help you can offer.
So I think perhaps, based on the answers so far (thanks everybody that has chimed in to help an amateur) - I think I am going to do a trial with:
if( ($r['freeFreight'] == 'ff') || ( $r['freeFreight'] == 'fw' ) && empty( $r['weight'] ) ) {
$r['shippingStandard'] = 0;
}
Planning to run trial with this, if it is fundamentally wrong, please advise.
Try this out?
if( $r['freeFreight'] == 'ff' || ( $r[ 'freeFreight' ] == 'fw' && empty( $r['weight'] ) ) ) {
$r['shippingStandard'] = 0;
}
using empty might be a little cleaner too
if you really want to break it out more, you could do this
if( $r[ 'freeFreight' ] == 'ff' ) {
$r[ 'shippingStandard' ] = 0;
} elseif( $r[ 'freeFreight' ] == 'fw' && empty( $r[ 'weight' ] ) ) {
$r[ 'shippingStandard' ] = 0;
} else {
// Everything else
}
If you want to shorten the variables, and will be using them later:
$freight = $r[ 'freeFreight' ];
$weight = isset( $r[ 'weight' ] ) ? $r[ 'weight' ] : null;
$shipping = $r[ 'shippingStandard' ]; // Or whatever you want the default value to be...
if( $freight == 'ff' || ( $freight == 'fw' && empty( $weight ) ) ) {
$shipping = 0;
}
// ... Later down the file
$r = array(
'freeFreight' => $freight,
'weight' => $weight,
'shippingStandard' => $shipping
);
I really cant tell how the rest of the file looks, like if this is in a function to get shipping, you could simply just return $shipping. Hope this helps. You should be able to move the concepts around to get what you want
your first option should should be identical to the following:
if ($r['freeFreight'] == 'ff') {
$r['shippingStandard'] = 0;
}
elseif ($r['freeFreight'] == 'fw') {
// the dual check for an empty value here is kind of redundant and unneccessary.
// I'd probably just use "if ($r['weight'])" and be done with it
if ($r['weight'] == 0) {
$r['shippingStandard'] = 0;
}
elseif ($r['weight'] == '') {
$r['shippingStandard'] = 0;
}
else {
// $r['shippingStandard'] stays whatever it was before
}
}
else {
// $r['shippingStandard'] stays whatever it was before
}
if that logic looks right to you, then you should be right on and I'd print_r($r) to make sure it's holding what you expect it to be holding. Your parenthesis in the first example is exactly how I would do it.
I have three variables which determine an outcome. There is only two outcomes but the outcome is based on the variables. I have thought up some long if statements but I am wondering if there is a cleaner way to do it.
$loggedin = (0 or 1) // If it is 0 then one outcome if 1 then it falls onto the next three variables
$status = (0-5) // 4 dead ends
$access = (0-3) //
$permission = (0-9)
Different combinations of the last two variables result in different outcomes, although some combinations are irrelevant as they are dead ends.
if ($loggedin == 1 && ($status == 1 || $status == 2 ) && 'whattodohere' ):
I could type all of the combinations manually ($access == 0 && ($var == 2 || $var = 6)) but I am wondering if there is a better way of doing this that I am unaware of.
Have a look at bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] ) - http://php.net/manual/en/function.in-array.php
Also take a look at range(...) - http://php.net/manual/en/function.range.php
$status == 1 || $status == 2 [... $status == n] can be reduced to in_array( $status, range(0, $n) )
Using in_array & range is more costly performance-wise tho, so if you're dead sure you only need to try against 2 different values, use == operator instead.
An approach could to be to use switch(): http://php.net/manual/en/control-structures.switch.php
Example:
<?php
/*
$loggedin = (0 or 1) // If it is 0 then one outcome if 1 then it falls onto the next three variables
$status = (0-5) // 4 dead ends
$access = (0-3) //
$permission = (0-9) */
$access = 1;
$loggedin = 1;
$status = 1;
if ($loggedin == 1) {
if ($status == 1 || $status == 2 ) {
switch($access) {
case 0:
//do some coding
break;
case 1:
echo 'ACCESSS 1';
//do some coding
break;
default:
//Do some coding here when $access is issued in the cases above
break;
}
}
}
else {
//Do coding when $loggedIn = 0
}
?>
In the example ACCESS 1 would be the output.
Maybe you could also do some math and compare the result (in some circumstances depending on what you want to achieve). For example:
<?php
$permission = 1;
$access = 2;
$result = $permission * $access;
if ($result > 0) {
switch($result) {
case 0:
//do something
break;
case 1:
//do something
break;
default:
//Do something when value of $result not issued in the cases above
}
}
?>
This should be a simple question. I have a simple if/else statement:
<?php
// TOP PICTURE DEFINITIONS
if ( is_page('english') ) {
$toppic = 'page1.png';
}
if ( is_page('aboutus') ) {
$toppic = 'page1.png';
}
if ( is_page('newspaper') ) {
$toppic = 'page1.png';
}
else {
$toppic = 'page1.png';
}
?>
Is there a difference from ^^^ to this:
<?php
// TOP PICTURE DEFINITIONS
if ( is_page('english') ) {
$toppic = 'page1.png';
}
elseif ( is_page('aboutus') ) {
$toppic = 'page1.png';
}
elseif ( is_page('newspaper') ) {
$toppic = 'page1.png';
}
else {
$toppic = 'page1.png';
}
?>
I should mention that this is going into Wordpress. And until now, I've used the first part (no elseif, just a series of 'ifs'), and it works. I was just curious to know what the difference was.
Thanks!
Amit
Yes. If a condition in an if/else control is satisfied, the rest of the checks will be omitted. else if is just a nested if inside an else!
if ( is_page('english') ) { // if true, other statements are skipped
$toppic = 'page1.png';
}
elseif ( is_page('aboutus') ) {
$toppic = 'page1.png';
}
elseif ( is_page('newspaper') ) {
$toppic = 'page1.png';
}
else {
$toppic = 'page1.png';
}
But in a series of ifs, all of them will be tested.
if ( is_page('english') ) {
$toppic = 'page1.png';
}
if ( is_page('aboutus') ) { // will be tested no matter what the outcome
// of the previous if statement was
$toppic = 'page1.png';
}
if ( is_page('newspaper') ) { // the same here
$toppic = 'page1.png';
}
else {
$toppic = 'page1.png';
}
So, if you're checking a property such as parity of a number, it's either odd or even, why do you want to bother checking other conditions if one is satisfied. It's a waste of resources. Therefore, the following code is much better
if(number_is_odd) {
}
else { // if it's not odd, it's even for sure
}
than
if(number_is_odd) {
}
if(!number_is_odd) {
}
Because the former checks the condition once whilst the latter does it twice. The same thing goes for conditions with more than two states.
The first method will check against every condition, whether they are true or false.
The second method will check against every condition until one is true, and then ignores the rest.
In your first block, every comparison in your block is executed. Also, toppic will always be assigned the value in is_page('newspaper') or the value in is_page('newspaper')'s else statement. This happens because the last if statment is always evaluated. Even if one of the previous if statements evaluated to true, you'll end up in the else block. To test this, try this code...
<?php
// TOP PICTURE DEFINITIONS
if ( is_page('english') ) {
$toppic = 'english.png';
}
if ( is_page('aboutus') ) {
$toppic = 'aboutus.png';
}
if ( is_page('newspaper') ) {
$toppic = 'newspaper.png';
}
else {
$toppic = 'finalelse.png';
}
?>
You'll always end with either 'newspaper.png' or 'finalelse.png'.
<?php
if ( 3 > 1 ) {
echo "This will be printed.";
}
if ( 3 > 2 ) {
echo "This will be printed too.";
}
if ( 3 > 3 ) {
echo "This will NOT be printed.";
}
else {
echo "This WILL be printed.";
}
?>
but with elseif:
<?php
if ( 3 > 1 ) {
echo "This will be printed.";
}
elseif ( 3 > 2 ) { /* This condition will not be evaluated */
echo "This will NOT be printed";
// because it's on the ELSE part of the previous IF
}
elseif ( 3 > 3 ) { /* This condition will not be evaluated either */
echo "This will NOT be printed.";
}
else { /* This ELSE condition is still part of the first IF clause */
echo "This will NOT be printed.";
}
?>
So you should use ELSEIF, because otherwise $toppic will always result on either 'newspaper.png', wich should be right, or 'finalelse.png' wich could be right or wrong, because it will overwrite the previous conditional clauses.
I hope you'll find this helpful.
It's not always just a question of efficiency. If you are toggling something, it is essential to use else if and not just if
Let's say we are toggling the variable $computerOn
if ($computerOn == true) {
$computerOn = false;
}
if ($computerOn == false) {
$computerOn = true;
}
In the case above your $computerOn will always be true. If it's true, it is set to false. After this, we check if it is false, which it now must be independent of initial conditions, so it is now set to true.
On the other hand the code below will toggle $computerOn:
if ($computerOn == true) {
$computerOn = false;
} elseif ($computerOn == false) {
$computerOn = true;
}
Now we only check whether $computerOn is false if it was not initially true. Hence we have a toggle.
If things get more complicated, you might have to use multiple elseifs. It's important to recognize when logic dictates that elseif is a must vs an option.
The biggest difference between the two is that the very last else block will be called whenever is_page('newspaper') returns false. In this case, it means just about every time the script runs. In this case, it's not a big deal, since you're only setting a variable, and it's the same value as everything else. But, if it were different, you would have a very frustrating bug to track down!
Besides that, if you use separate if statements, the condition for each if is evaluated every time. Again, in this case, it's (probably) not a big deal. But, if the condition was, say...
if(delete_file('foo.png')) {
....
}
if(delete_file('bar.png')) {
....
}
if(delete_file('baz.png')) {
....
}
else {
....
}
Well, you should be able to see where this is going ;) If you use elseif, it will stop trying to evaluate once it gets a true. And, the else will only be called if nothing else is true.
The answer is simple:
if(a==1){
b
}
elsif(b==1){
c
}
equals to
if(a==1){
b
}
else{
if(b==1){
c
}
}
This is the same as
if(a==1){
b
}
if(b==1){
c
}
if it is not possible that a==1 and b==1 at the same time. Although when both if statements can be true, when b and c can be executed. This would not be possible if you use elsif there, because b==1 would only be checked if a!=1!
Use elseif wisely can save you a bunch of time since the parser doesn't need to evaluate all the conditions.