This question already has answers here:
Stacking Multiple Ternary Operators in PHP
(11 answers)
Closed 2 years ago.
I am fairly capable at using the PHP ternary operator. However I have hit a roadblock at trying to figure out why the code below does not match the if-else equivalent structure. The test was run three times on different numbers. The output for each structure is below the code.
Ternary:
$decimal_places = ($max <= 1) ? 2 : ($max > 3) ? 0 : 1;
Ternary Output:
max: -100000 decimal: 0
max: 0.48 decimal: 0
max: 0.15 decimal: 0
If-Else
if($max <= 1)
$decimal_places = 2;
elseif($max > 3)
$decimal_places = 0;
else
$decimal_places = 1;
If-Else Output:
max: -100000 decimal: 2
max: 0.48 decimal: 2
max: 0.15 decimal: 2
Can anyone please tell me why the these two control stuctures do not output the same data?
Your right-hand-side ternary expression needs to be wrapped in parentheses so it'll be evaluated by itself as a single expression:
$decimal_places = ($max <= 1) ? 2 : (($max > 3) ? 0 : 1);
// Another way of looking at it
$decimal_places = ($max <= 1)
? 2
: (($max > 3) ? 0 : 1);
Otherwise your ternary expression is evaluated from left to right, resulting in:
$decimal_places = (($max <= 1) ? 2 : ($max > 3)) ? 0 : 1;
// Another way of looking at it
$decimal_places = (($max <= 1) ? 2 : ($max > 3))
? 0
: 1;
Which, translated to if-else, becomes this:
if ($max <= 1)
$cond = 2;
else
$cond = ($max > 3);
if ($cond)
$decimal_places = 0;
else
$decimal_places = 1;
Therefore $decimal_places ends up as 0 for all values of $max except 2, in which case it evaluates to 1.
The code is executed as
$decimal_places = (($max <= 1) ? 2 : ($max > 3)) ? 0 : 1;
so you'll never get 2 and 1 only when 1 < $max <=3. This is because the conditional operator is left-associative. Solution: Place parentheses to make sure the order you want is coded:
$decimal_places = ($max <= 1) ? 2 : (($max > 3) ? 0 : 1);
Just put the parenthesis and you would be fine, like this:
$decimal_places = ($max <= 1) ? 2 : (($max > 3) ? 0 : 1);
As others pointed out, use paranthesis.
However, if you actually want to make it readable, what about this:
$decimal_places =
($max <= 1) ? 2 : (
($max > 3) ? 0 : (
1
));
This still looks super awkward, but this awkwardness has a regular shape, so it's easier to live with.
$drink = 'wine';
return
($drink === 'wine') ? 'vinyard' : (
($drink === 'beer') ? 'brewery' : (
($drink === 'juice') ? 'apple tree' : (
($drink === 'coffee') ? 'coffeebeans' : (
'other'
))));
You could of course omit the last pair of brackets, but that would make it less regular-looking.
Related
Code:
<?php
$a = 200;
$b = 300;
if ($a > $b + $b != 3)
print "Correct";
else
print "Incorrect";
?>
output is: Correct
Can someone help me understand why the output became "Correct"?
To understand what is happening here, you need to look at the list of operator precendence to see what is being evaluated first. It's not left to right. The order of the operators in your if statement are as follows:
+ - ++ -- ~ (int) (float) (string) (array) (object) (bool) # - arithmetic (unary + and -), increment/decrement, bitwise, type casting and error control
< <= > >= - associative comparison
== != === !== <> <=> - non associative comparison
So in essence, your if statement breaks down to this:
(($a > ($b + $b)) != 3)
With your values becomes
((200 > (300 + 300)) != 3)
((200 > 600) != 3)
(false != 3)
So of course, false is not 3, and makes your if statement correct. If you want to evaluation 200 is greater than 300 AND 300 is not 3, then you need the logical AND operator, or &&, which would be
($a > $b && $b != 3)
which would print Incorrect
I'm trying to create a more simple version of if/else php statements because there are very many of them, so I want to make the code shorter and simpler.
I thought since each outputted value will equal a number going up from 0, then maybe I can do an array, but I'm also open to other solutions.
In human language, I want the value to be one of these numbers between 0 and 4 if it is within different parts of a range of numbers:
// 4 = 10,000+
// 3 = 1000-9,999
// 2 = 500-999
// 1 = 100-499
// 0 = <100
e.g. if $aa is 547, then $zz should equal the numeric value of 2, because it falls within 500-1000.
Now using if/else it's possible, and that works, but I want to make it shorter. Here it is in if/else statements:
if ( $aa >= 10000 ) {
$zz = 4;
} else if ( $aa >= 1000 && $aa < 10000 ) {
$zz = 3;
} else if ( $aa >= 500 && $aa < 1000 ) {
$zz = 2;
} else if ( $aa >= 100 && $aa < 500 ) {
$zz = 1;
} else {
$zz = 0;
}
Now I tried making an array but having trouble figuring out how to do this. Here's what I started with:
$b4 = $aa >= 10000;
$b3 = $aa >= 1000 && $aa < 10000;
$b2 = $aa >= 500 && $aa < 1000;
$b1 = $aa >= 100 && $aa < 500;
$b0 = $aa < 100;
$b_val = array( $b0, $b1, $b2, $b3, $b4 );
This is terribly wrong I know. It's not going to work like that, but maybe there's a way to make it work.
I thought of using switch, but it seems switch isn't designed for this, even though it can be done, and it isn't any shorter. I thought of using ternary, but that doesn't seem shorter either, unless you know how.
How to get a short, crisp, clean code to shorten the if/else statement on ranges of numbers?
You can use a nested ternary working smallest to largest
$zz = $aa < 100 ? 0 : ($aa < 500 ? 1 : ($aa < 1000 ? 2 : ($aa < 10000 ? 3 : 4)));
Demo ~ https://3v4l.org/MnqQv
I'd argue it's not as readable and therefore not as good as a plain old if..elseif..else or a function that returns at the appropriate logic branch.
function getRangeIndex($aa) {
if ($aa < 100) return 0;
if ($aa < 500) return 1;
if ($aa < 1000) return 2;
if ($aa < 10000) return 3;
return 4;
}
I have a few strings stored in a database which contain specific rules which must be met. The rules are like this:
>25
>25 and < 82
even and > 100
even and > 10 or odd and < 21
Given a number and a string, what is the best way to evaluate it in PHP?
eg. Given the number 3 and the string "even and > 10 or odd and < 21" this would evaluate to TRUE
Thanks
Mitch
As mentioned in the comments, the solution to this can be very simple or very complex.
I've thrown together a function that will work with the examples you've given:
function ruleToExpression($rule) {
$pattern = '/^( +(and|or) +(even|odd|[<>]=? *[0-9]+))+$/';
if (!preg_match($pattern, ' and ' . $rule)) {
throw new Exception('Invalid expression');
}
$find = array('even', 'odd', 'and', 'or');
$replace = array('%2==0', '%2==1', ') && ($x', ')) || (($x');
return '(($x' . str_replace($find, $replace, $rule) . '))';
}
function evaluateExpr($expr, $val) {
$x = $val;
return eval("return ({$expr});");
}
This supports multiple clauses separated by and and or, with no parentheses and the and always being evaluated first. Each clause can be even, odd, or a comparison to a number, allowing >, <, >=, and <= comparisons.
It works by comparing the entire rule against a regular expression pattern to ensure its syntax is valid and supported. If it passes that test, then the string replacements that follow will successfully convert it to an executable expression hard-coded against the variable $x.
As an example:
ruleToExpression('>25');
// (($x>25))
ruleToExpression('>25 and < 82');
// (($x>25 ) && ($x < 82))
ruleToExpression('even and > 100');
// (($x%2==0 ) && ($x > 100))
ruleToExpression('even and > 10 or odd and < 21');
// (($x%2==0 ) && ($x > 10 )) || (($x %2==1 ) && ($x < 21))
evaluateExpr(ruleToExpression('even and >25'), 31);
// false
evaluateExpr(ruleToExpression('even and >25'), 32);
// true
evaluateExpr(ruleToExpression('even and > 10 or odd and < 21'), 3);
// true
Why don't you translate the string even to maths? If you use mods you can write it like that $number % 2 == 0. In that case, your example will be:
if(($number % 2 == 0 && $number > 10 ) || ($number % 2 != 0 && $number < 21)){
//Then it is true!
}
I need a little help with an if statement in php. I'm trying to set a variable called offset according to a page that I am loading in WordPress. Here's the variable:
$offset = ($paged * 6);
What it does is it loads the first page, which is:
http://example.com/blog
and $offset is thus set to 0 because $paged is referring to the appending number on the URL. The second page, for example is:
http://example.com/blog/2/
which makes $offset set to 12. The problem is, I need the second page to define $offset as 6, the third page to define $offset as 12, etc. I tried using:
$offset = ($paged * 6 - 6)
which works except on the first page. On the first page it defines $offset as -6. SO, I wanted to create an if statement that says if $paged is equal to 0 then $offset is equal to 0, else $offset is equal to ($paged * 6 - 6).
I struggle with syntax, even though I understand what needs to be done here. Any help would be greatly appreciated. Thanks!
Because this is two different cases which cannot be easily integrated into a single formula, use an if statement:
if ($paged == 0)
$offset = 0;
else
$offset = ($paged - 1) * 6;
You can write this shorter using the ternary operator, but I think the above if statement is more readable:
$offset = ($paged == 0) ? 0 : ($paged - 1) * 6;
An alternative:
if($paged == 0) $paged = 1;
$offset = ($paged - 1) * 6;
or
$offset = ($paged) ? ($paged - 1) * 6 : 0;
you can use the following one line code
$offset = max(($paged - 1) * 6, 0);
You could use: $offset = max(0, $paged * 6 -6);. Which takes the maximum of 0 and the other value. If the other value is negative, then 0 will be the result.
Now, to use an if statement, this is how you would do it:
$offset = $paged * 6 - 6;
if ($offset < 0) {
$offset = 0;
}
Or:
if ($paged == 0) {
$offset = 0;
} else {
$offset = $paged * 6 - 6;
}
And you might want to use a ternary operator for this simple case:
$offset = ($paged == 0)? 0: ($paged * 6 - 6);
(which does the exact same thing as the above)
Note: You could replace 6*a-6 = 6*(a-1), but it's of no importance in the code, just readability...
I have something like
just a snipplet
$i = 1; while (...) {
echo ($i % 5 == 1) ? 'class="first-col"' : ($i % 5 == 0) ? 'class="last-col"' : '';
$i++;
}
but even when $i % 5 == 1, I will get class="last-col" echo-ed is my logic right?
This is actually a CSS fix for IE so that I wont need to use nth-child. I am trying to target the 1st and last columns of my grid which contains 5 col/row
The ?: operator is left-associative, i.e. you have
echo ( ($i % 5 == 1) ? 'class="first-col"' : ($i % 5 == 0) ) ? 'class="last-col"' : '';
See http://php.net/manual/en/language.operators.comparison.php
It is best not to nest ternary operators.
Better use if / elseif / else constructs, they are more legible.
adding parenthesis helps:
echo (($i % 5 == 1) ? 'class="first-col"' : (($i % 5 == 0) ? 'class="last-col"' : ''));
For a start you're using nested ternary operations. I'd at least use brackets around the individual conditions to make it obvious what should be carried out first.