How to make variable value available in a second nested while loop? - php

I currently have a while loop which has another while loop running inside of it, I want to reference a value which is changed by the first loop each time the second loop is run.
$counter = 0;
$counter2 = 2;
while ($counter < 7) {
$name = 0;
while ($array[$counter2][2] == !false) {
if ($array[$counter2][3] == "string") {
if ($array[$counter2][2] == $counter) {
$name = 1;
}
}
$counter2++;
}
$counter++;
}
When I run the above and do the second IF statement the value of $counter is 0 even though this should go up through each iteration of the first loop.
How do I get the value of $counter in the second while loop to match the $counter I am using in the first while loop?

The second loop was only ever running once because the array search would always start off as false due to the fact I wasn't resetting the second counter each time the first loop was run.

Related

What does this for-loop mean?

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?

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.

Stuck with php foreach loop - need to run the loop with the same key once more

Well I'm stuck with this one.
I have a foreach loop nested in another foreach loop. Now on certain conditions I need to run the outer loop once again with the same key.
While there is this function prev($array), which would set the iterator to the previous position, this does not seem to work and the php docs says
As foreach relies on the >internal array pointer, changing it within the loop may lead to unexpected behavior.
The array I am working on is an associative array, so obviously I cannot easily switch to integer indexed iterations.
$arrLocalCopy = $this->arrPrintOut;
$groupCounter = 0;
foreach($this->arrPrintOut as $kOne => $rowA){
//first and last row contain titles and totals, so we skip them
if($groupCounter < 1 || $groupCounter == sizeof($this->arrPrintOut)-1 ){ $groupCounter++; continue; }
$rowCounter = 0;
foreach($arrLocalCopy as $k => $rowB){
//skip rows that are "before" the outer loops row, as we want to compare only rows below the row we compare to.
if ($rowCounter <= $groupCounter || $rowCounter == sizeof($arrLocalCopy)-1 ) { $rowCounter++; continue; }
//If those values are the same, then values belong to the same group and must be summed together.
if($rowA['t']==$rowB['t'] && $rowA['y']==$rowB['y'] && $rowA['g']==$rowB['g'] && $rowA['q']==$rowB['q'])
{
//if values are the same, then data belongs to one group, lets group them together.
$this->arrPrintOut[$kOne]['s'] += $rowB['s'];
$this->arrPrintOut[$kOne]['b'] += $rowB['b'];
$this->arrPrintOut[$kOne]['v'] += $rowB['v'];
$this->arrPrintOut[$kOne]['r'] += $rowB['r'];
$this->arrPrintOut[$kOne]['k'] += $rowB['k'];
$this->arrPrintOut[$kOne]['n'] += $rowB['n'];
$this->arrPrintOut[$kOne]['m'] += $rowB['m'];
$this->arrPrintOut[$kOne]['l'] += $rowB['l'];
unset($this->arrPrintOut[$k]); //row has been grouped to the current row, so we remove it from the array and from the copy.
unset($arrLocalCopy[$k]);
prev($this->arrPrintOut); //we need to run the outer loop with the same key again, as probably there is another row which could be grouped together with this row.
$groupCounter--;
}
$rowCounter++;
}
$groupCounter++;
}
Here is a code sample.
$rollback is a variable to check if we prev() already
$max is the number of iterations. If you prev() you have to increment it.
<?php
$array = array(1 => "a", 6 => "b", 19 => "c");
$rollback=0;
$max = sizeof($array);
for ($i=0; $i<$max; $i++) {
$key = key($array);
$value = $array[$key];
print "$key => $value\n";
next($array);
if ($value == "b" && $rollback == 0) {
prev($array);
$rollback = 1;
$max++;
}
}

php how to loop through array until condition is met?

I have a database table with images that I need to display. In my view, I'd like to display UP TO 10 images for each result called up. I have set up an array with the 20 images that are available as a maximum for each result (some results will only have a few images, or even none at all). So I need a loop that tests to see if the array value is empty and if it is, to move onto the next value, until it gets 10 results, or it gets to the end of the array.
What I'm thinking I need to do is build myself a 2nd array out of the results of the test, and then use that array to execute a regular loop to display my images. Something like
<?php
$p=array($img1, $img2.....$img20);
for($i=0; $i<= count($p); $i++) {
if(!empty($i[$p])) {
...code
}
}
?>
How do I tell it to store the array values that aren't empty into a new array?
you could do something like:
$imgs = array(); $imgs_count = 0;
foreach ( $p as $img ) {
if ( !empty($img) ) {
$imgs[] = $img;
$imgs_count++;
}
if ( $imgs_count === 10 ) break;
}
You can simply call array_filter() to get only the non-empty elements from the array. array_filter() can take a callback function to determine what to remove, but in this case empty() will evaluate as FALSE and no callback is needed. Any value that evaluates empty() == TRUE will simply be removed.
$p=array($img1, $img2.....$img20);
$nonempty = array_filter($p);
// $nonempty contains only the non-empty elements.
// Now dow something with the non-empty array:
foreach ($nonempty as $value) {
something();
}
// Or use the first 10 values of $nonempty
// I don't like this solution much....
$i = 0;
foreach ($nonempty as $key=>$value) {
// do something with $nonempty[$key];
$i++;
if ($i >= 10) break;
}
// OR, it could be done with array_values() to make sequential array keys:
// This is a little nicer...
$nonempty = array_values($nonempty);
for ($i = 0; $i<10; $i++) {
// Bail out if we already read to the end...
if (!isset($nonempty[$i]) break;
// do something with $nonempty[$i]
}
$new_array[] = $p[$i];
Will store $p[$i] into the next element of $new_array (a.k.a array_push()).
Have you thought about limiting your results in the sql query?
select * from image where img != '' limit 10
This way you are always given up to 10 results that are not empty.
A ẁhile loop might be what you're looking for http://php.net/manual/en/control-structures.while.php

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