What does this for-loop mean? - php

I've stumbled upon a syntax in a code example I have never seen in for-loops before in PHP.
What does this do? WHY should I use this?
for(;$i<$max;){
$i++;
//code..
}
I could figure out that it was not the same as
for($i=0;$i<$max;$i++) {
//code...
}
I don't understand the difference between the two examples above.
If for being more specific about my thoughts.
If we have this code (taken from a solution from adventofcode):https://www.reddit.com/r/adventofcode/comments/kdf85p/2020_day_15_solutions/
<?php
$cap = 2020;
$bits = [5,1,9,18,13,8,0];
$i=0;
$time = [];
foreach($bits as $bit) {
$i++;
$time[$bit] = $i;
$say = 0;
}
for(;$i<$cap-1;){
$i++;
if(isset($time[$say])) {
$last = $time[$say];
}
else {
$last = $i;
}
$time[$say] = $i;
$say = $i - $last;
}
echo $say;
?>
and compare that to:
<?php
$cap = 2020;
$bits = [5,1,9,18,13,8,0];
$i=0;
$time = [];
foreach($bits as $bit) {
$i++;
$time[$bit] = $i;
$say = 0;
}
for($i=0;$i<$cap-1;$i++){
if(isset($time[$say])) {
$last = $time[$say];
}
else {
$last = $i;
}
$time[$say] = $i;
$say = $i - $last;
}
echo $say;
?>
I get different results in $say.(376 in first example and 38 in last example).
Why do I get different values?

Well based on the PHP docs:
https://www.php.net/manual/en/control-structures.for.php
for loops are the most complex loops in PHP. They behave like their C
counterparts. The syntax of a for loop is:
for (expr1; expr2; expr3)
statement
While:
Each of the expressions can be empty or contain multiple expressions
separated by commas.
So it's just a way to "save code" when your first expression of the for-loop is "obvious", So instead of mentioning the default obvious expression - you just "skip" it.
According to your example:
for(;$i<$cap-1;)
we skipped the first expression (expr1) as $i has been already defined as 0 ($i = 0;) earlier in the code block so it's "skippable". Doesn't affect the code.
But, The third expression:
At the end of each iteration, expr3 is evaluated (executed).
Since we don't mention it in the loop, we are responsible to handle the "increasement" (in this case) of $i in the loop itself.
However - the main difference in the code is that if you mention the third expression - it's evaluated at the end of each iteration but in the code block (your example) - we increase the $i variable at the beginning of the code.

The main difference between the two blocks (assuming $i is initialised to 0 before the for(;$i<$max;){ loop) is that the first loop increments $i before running the loop code, where the second loop increments $i after running the loop code. As a trivial example:
function code($i) {
echo "$i\n";
}
$max = 3;
$i = 0;
for(;$i<$max;){
$i++;
code($i);
}
for($i=0;$i<$max;$i++) {
code($i);
}
The output of the first loop is:
1
2
3
while the output of the second loop is:
0
1
2
Note (as pointed out by #IMSoP), the condition clause is executed at the beginning of the loop, and can have side-effects, so you could also emulate the first loop with this code:
for($i=0;$i++<$max;) {
code($i);
}

C-style for loops are a rather peculiar piece of syntax. They actually consist of three expressions, any of which can do anything you like, or even be empty:
An expression to execute for its side effects once before the loop begins
An expression to evaluate at the beginning of each iteration, to see if the loop should terminate
An expression to execute for its side effects at the end of each iteration
The most common way to use them is:
Initialise a counter of some sort
Check if the counter has reached some value
Increment or decrement the counter
But those aren't built into the language at all, and leaving one of the expressions out doesn't apply any default behaviour, it just does nothing - except that leaving the second expression empty always evaluates to true.
So, for instance:
for(;true;) is an infinite loop: it does nothing, checks true, and does nothing again
for(;;) is the same infinite loop, because the empty expression in the middle is considered true
for($i=0;;) is just the same infinite loop, but with $i initialised to 0 before running it
for(;true;foo()) is the same loop, but with the function foo() run at the end of every iteration
In your case, the loop is for(;$i<$max;) which breaks down to:
Before starting the loop, do nothing
At the beginning of each iteration, terminate if the expression $i<$max happens to be false
At the end of each iteration, do nothing
It doesn't do anything to control what values $i and $max have before or during the loop.
In fact, it's just the same as while($i<$max) and would probably be much clearer if written that way.
The example shown seems to have ended up that way because the author wanted to move the $i++ to the beginning of each iteration, rather than the end (although why they also left out the $i=0 I'm not sure). But the for syntax supports that too! You can actually include multiple expressions with a comma between in any of the three positions; if they're in the second position, the last one is what determines if the loop exits.
So you can do this:
for($i=0; $i++,$i<=$max; )
Now, $i will be incremented at the beginning of the loop; since it will be incremented before the test, you have to also adjust the condition from < to <= to make sure it runs for one more iteration.
For that particular case, there's another way as well: incrementing a variable returns a value. If you write ++$i it returns the value after incrementing, and if you write $i++ it returns the value before incrementing. So both of these would also work:
for($i=0; ++$i <= 10; ) echo $i, PHP_EOL;
for($i=0; $i++ < 10; ) echo $i, PHP_EOL;

In your first example, $i is registered somewhere before. so $i can be 0 or other zero, because if you use code like
for(;$i<$max;){
$i++;
//code..
}
you got error Undefined variable $i in and later $i is registered.
/* example 1 */
for ($i = 1; $i <= 10; $i++) {
echo $i;
}
/* example 2 */
for ($i = 1; ; $i++) {
if ($i > 10) {
break;
}
echo $i;
}
/* example 3 */
$i = 1;
for (; ; ) {
if ($i > 10) {
break;
}
echo $i;
$i++;
}
https://www.php.net/manual/en/control-structures.for.php
what is your point?

Related

PHP skip for loop Iteration based on a query result set

Lets say for example I have a result set from a MySQL query that has produced values: 3,5,10,11 and so forth...
I would like to iterate through a for loop in PHP but exclude the any iteration that would equal any number in results of my MySQL query.
As it stands I currently have:
for($i = 1; $i <= $num_rows; $i++)
{
if($i == 3 or $i == 5)
{
continue;
}
//Rest of loop...
As you will all appreciate hard coding these values is very time consuming and not very efficient. If any one could help it would be greatly appreciated.
If you can gather automatically those values you are currently hard coding, you can use in_array($search, $array[, $strict])
Like this :
$bypass = array(3, 5);
for($i = 1; $i <= $num_rows; $i++)
{
if( in_array($i, $bypass) )
{
continue;
}
// rest of the loop
}
PS : I prefer much more the "Dont Panic" answer, which doesn't use too many loops. (in_array will loop through the array to find your value). See his answer : https://stackoverflow.com/a/38880057/3799829
If your query results are returned as an array you can use
if(in_array($i, $results)) to do a check of $i against the results
Add your values into an array while you fetch your query results. I used PDO here for example, but you should be able to adapt it to whichever database extension you're using:
while ($row = $stmt->fetchObject()) {
$exclude[$row->value] = true;
// whatever else you're doing with the query results
}
If you use the values as keys in the array of things you want to skip, checking for them in your for loop should be more efficient than using in_array.
for($i = 1; $i <= $num_rows; $i++) {
if (isset($exclude[$i])) continue;
// rest of loop

Foreach into PHP array

I've this these array values :
$cart_item['addons'][0]['price'] = '52';
$cart_item['addons'][1]['price'] = '34';
$cart_item['addons'][2]['price'] = '12';
......
....
I want that each values are at 0 like :
$cart_item['addons'][0]['price'] = '0';
$cart_item['addons'][1]['price'] = '0';
$cart_item['addons'][2]['price'] = '0';
....
...
So I try this code :
for ($i=0; $i > 0 ; $i++) {
$cart_item['addons'][$i]['price'] = '0';
}
But it does not work. Thanks for your help !
Try this simple solution:
$count=count($cart_item['addons']);
for($i=0; $i<$count;$i++ ){
$cart_item['addons'][$i]['price'] = '0';
}
If your array is big enough, putting count() function inside for loop is being a crazy coconut. It will be much, much slower. Please use the count outside the loop:
$count = count($cart_item['addons'])
for($i=0; $i<$count;$i++ ){
$cart_item['addons'][$i]['price'] = '0';
}
You have to loop more often to achive this:
foreach($cart_item['addons'] as &$addons {
foreach($addons as &$addon) {
$addon['price'] = 0;
}
}
You can iterate over $cart_item['addons'], like so:
foreach ($cart_item['addons'] AS $key => &$value) {
$value['price'] = 0;
}
(Your code does not get executed, because $i is never changed and so is never > 0)
Also note that you need to use the reference (&$value) when changing the array in foreach loop.
There are three parts to your for loop: for(counter | test | action){}. It might be useful for you to look at this guide about for loops. You initialise your variable:
$i = 0;
then you do a logical check (the test part):
$i > 0;
If we substitute the the variable ($i) for the value it holds (0) we get:
0 > 0
which will never be true and thus you will never get to the final part (action) of the for loop:
$i++;
Instead you could make it loop until it has moved through your entire array like this:
$elementCount = count(cart_item['addons']);
for($i=0; $i < $elementCount; $i++){
$cart_item['addons'][$i]['price'] = '0';
}
Each time it loops we add one to $i until we reach the stopping condition where $i is no longer less than the number of items in the array.
It's also worth noting that PHP has a number of functions that help working with arrays.

Issue with getting data when traversing through an array PHP with a variable

I asked a similar question earlier but I couldn't get a clear answer to my issue. I have a function "isParent" that gets 2 pieces of data. Each 1 of the 2 gets a string separating each value with a , or it just gets a plain int and then checks if the first value given is a parent of the second.
I pull the 2 bits of data in and explode them but when I go through my nested for loop and try to test
$toss = $arr1[$i];
print_r($toss);
It comes up blank. I have no idea what the issue is: Here is the full code of the function...
function isParent($parent, $child)
{
$parentArr = explode(',', $parent);
$childArr = explode(',',$child);
//Explode by Comma here. If array length of EITHER parentArr or childArr > 1 Then throw to an Else
if(count($parentArr) <= 1 && count($childArr) <= 1) //If explode of either is > 1 then ELSE
{
$loop = get_highest_slot(15);
for($i = $loop; $i > 0; $i--)
{
$temp = get_membership_from_slot($i,'id_parent','id_child');
if($temp['id_parent'] == $parent && $temp['id_child'] == $child)
{
return 1;
}
}
}
else //set up a for loop in here so that you traverse each parentArr value and for each iteration check all child values
{
$i = count($parentArr);
$c = count($childArr);
for(;$i >=0;$i--) //Loop through every parent
{
for(;$c >=0;$c--)
{
echo '<br>$i = ';
print_r($i);
echo '<br><br>Parent Arr at $i:';
$toss = $parentArr[$i];
echo $toss;
echo '<br>';
print_r($childArr);
echo '<br><br>';
if(isParent($parentArr[$i],$childArr[$c])) //THIS CAUSES AN INFINITE YES! Learn how to pull an array from slot
{
return 1;
}
}
}
}
return 0;
}
You are missing some code for the slot procedures. Apart from that, you probably need to use a different variable for the inner for loop. because $c will be 0 after the first iteration of $i.
Thanks for the help! The issue was in the recursive call back to the top of the function. It was tossed empty slots and when comparing 2 empty slots it returned a false positive. A quick !empty() check fixed it.

for() loop fails to stop looping ?

I'm trying to print an array until it's empty.
Here is my code:
for ($i=0; $array[0][$i]!=NULL; ++$i){
echo $array[0][$i];
}
However it looks like it performs the echo one extra time, I don't know why ?
Here's my output for an array that contains data up to array[0][2].
I am sure that array[0][3] is empty, I tried it with if(array[0][3]==NULL)
Test 0
Test 1
Test 2
( ! ) Notice: Undefined offset: 3 in C:\... on line 9
Any idea ?
You should use the arrays length when you loop... Or try this instead:
foreach($array[0] as $val) {
echo $val;
}
It makes perfect sense, really. PHP can not magically know that the next id (in your example, index number '3') is not set, it has to access the variable to determine that. That's also why you get the notice.
In short, it does not execute the body of the for loop, but it has to execute the test to determine whether or not to continue.
Anyway, use a foreach loop, or calculate the number of items in the array first and increment $i till they match.
Possible alternatives to you code that should actually work.
foreach($array[0] as $val)
if($val===null)
break;
else
echo $val;
or
$arrlen=count($array[0]);
for($i=0;$i<$arrlen;$i++)
if($array[0][$i]===null)
break;
else
echo $array[0][$i];
or even
$i=0;
while($array[0][$i]!==null) { //not recommended, can cause infinite loop
echo $array[0][$i];
$i++;
}
The reason why it's throwing an error is because an array index that does not exist is not equal to NULL. You can modify your code to this:
for ($i=0; isset($array[0][$i]); ++$i){
echo $array[0][$i];
}
Or try this instead:
$ctr = count($array[0]);
for ($i = 0; $i < $ctr; $i++) {
echo $array[0][$i];
}
The loop should finish after reaching the end of the array.

Break levels in PHP

I have this snippet of code and it's working just fine (because in my example that variable really exists in $arrival_time so it breaks at about $k = 10).
$arrival_time = explode(",", $arrival_timeAll[1]);
$sizeOfArrival = sizeof($arrival_time);
$k = -1;
while (++$k < $sizeOfArrival) {
if ($arrival_time[$k] >= $someVariable) {
break;
}
}
Isn't that the same as this code? I added the while(true) loop and increased the "break level" - so it's now break 2, not just break. But seems that's an infinite loop. Why?
while (true) {
$arrival_time = explode(",", $arrival_timeAll[1]);
$sizeOfArrival = sizeof($arrival_time);
$k = -1;
while (++$k < $sizeOfArrival) {
if ($arrival_time[$k] >= $someVariable) {
break 2;
}
}
}
Why adding while(true)? Because I need to define some more statements (which here aren't neccesary for explanation) if inside while loop doesn't find the matching one (if that "break" in first case, "break 2" in second case doesn't run).
Anyway - why this isn't working?
In some cases, the predicate for the inner while loop is never hit, thus the while loop is never executed. As you don't have a break after it, the loop will run forever. This case would be if the array is empty, 0 < 0 is false.
Starting with $k = -1, the first time the predicate gets evaluated, $k will be 0 as you are using the preincrement operator, for an empty array this will evaluate to false and the code with run infinite. Without the while(true) loop this wouldn't cause any problems as you'd just jump straight over it.

Categories