I have a foreach loop inside another foreach loop as this code:
foreach($array1 as $uid => $somevalues) {
$status = true;
foreach($somevalues as $somevalue)
if(!isset($somevalue->$someothervalue))
$status = false;
if($status) {
$content .= "some content added";
$count++;
}
}
How it works
As you see I am looping through all entries in $array1. For each entry I loop through the contained $somevalues array to look for a certain condition (that $someothervalue exists there). Now, if this condition (that something isset) is not true then the $status changes to false.
Only if the $status is still true after all loops of the inner foreach, I wish some action to be made (some content added to a text string $content and a raised counter).
The goal
The arrays I'm working with can be quite large and I have been looking at the break and continue commands to try to skip unnecessary parts to improve the code and have the script running through fewer loops.
As you can see, if a loop from the inner foreach results in $status=false then we can stop it right there (no further loops here are necessary). The break is good for this to skip the rest of the forearch loops.
If this is the case then we can also skip the rest of the current outer loop from the outer foreach, since there will not be run any code now that $status is false. For this purpose the continue command would be useful to let us skip the rest of the current outer loop and go on to the next array row right away.
The question
I have been looking at the command lines break 2 and continue 2 e.g. that let you skip the current AND the outer loop (two steps up) with their respective results (either stopping the loop entirely or skipping the rest of current loop). They work as double break - as a "break break" - and as double continue - as a "continue continue" - respectively.
But here I need something like break continue instead, if you get my point. The first loop should break, the next should continue. I am trying to avoid reaching the if statement in the code if not necessary. The check is already made and it feels like doing an unnecessary double check.
I hope the question is clear. Is there a method for this purpose to double skip from within the inner foreach but with the effect of two different commands break continue?
"Flag" variables are mostly a code smell, and an indication that you actually should be using a function:
function all_valid($somevalues) {
foreach($somevalues as $somevalue)
if(!isset($somevalue->$someothervalue))
return false;
return true;
}
foreach($array1 as $uid => $somevalues) {
if(all_valid($somevalues) {
do stuff
If you're prepared to sacrifice readability for performance, then how about:
foreach($array1 as $uid => $somevalues) {
foreach($somevalues as $somevalue)
if(!isset($somevalue->$someothervalue))
goto next; // I don't believe I wrote this
$content .= "some content added";
$count++;
next:
}
Just use continue 2, it will 'break' out of the first loop and continue the outer loop.
foreach([1, 2, 3] as $i) {
echo $i . PHP_EOL;
foreach([4, 5, 6] as $ii) {
continue 2;
}
echo 'this will never print' . PHP_EOL;
}
Will print:
1
2
3
foreach($array1 as $uid => $somevalues) {
// Task of loop 1
foreach($somevalues as $somevalue){
// Task of loop 2
if(!isset($somevalue->$someothervalue))
{
continue 2;
break;
}
}
}
Related
I am writing some code which will frequently contain loops that I need to break out of under certain conditions. The problem is that sometimes, I am nested 3 levels deep, some times 4 and some times 5 or more.
I could break out by writing break 3, break 4 and break 5 etc. but that requires me to keep track of the current depth inside each nested loop. Is there an easier way of breaking out of unknown number of nested loops without using goto?
I think the question needs a little more explanation.
There are 4 nested for loops in part of a code.
Somewhere down the line there are 3 nested for loops.
Then somewhere else there are 5 nested for loops.
There are all independent of each other and I only have to go through one of them for each run of the code. The one I loop through depends on value of certain parameters.
I could break out of them by keeping track of for loops in each case and using break number but doing it for 100-200 different sets of nested for loops is tiring. It will also not work if the number of nested loops have to be updated.
I was hoping that there is some code in PHP which could just break out of all loops at once without me keeping track.
If you know the depth of the loop you want to break out of, but not how many levels down you are from that, you can use a variable that each loop checks.
$break_level = 99;
while (...) {
while (...) {
while (...) {
while (...) {
...
if (...) {
$break_level = 2;
break;
}
...
}
if ($break_level <= 3) {
break;
}
}
if ($break_level <= 2) {
break;
}
}
if ($break_level <= 1) {
break;
}
}
But this kind of generality is hardly ever needed. I've written millions of loops in my lifetime, and I hardly ever need to break out of anything other than the current loop, its immediate container, or the entire set of nested loops. In these cases there's often a variable that can be checked. For instance, if you're searching a multidimensional array, just set a $found variable:
$found = false;
foreach ($array as $level1) {
foreach ($level1 as $level2) {
if (...) {
$found = true;
break;
}
}
if ($found) {
break;
}
}
I have 2 Foreach-Loops. One of them is nested inside the other, e.g.:
foreach(...){
foreach(...){
//if statement
}
}
Within the inner loop I got an if statement and if this statement returns true I want to break the inner loop and continue with the next outter loop. Is there any way to realize this? "break 2; " won't work as I need to continue with the outter loop.
Like with break, you can add a number to continue as well:
foreach(...) {
foreach(...) {
if (...)
continue 2;
}
// this will be skipped
}
From the docs:
continue accepts an optional numeric argument which tells it how many
levels of enclosing loops it should skip to the end of. The default
value is 1, thus skipping to the end of the current loop.
Per PHP documentation, the default is 1, which only breaks out of the immediately encapsulating control struct
break ends execution of the current for, foreach, while, do-while or
switch structure.
break accepts an optional numeric argument which tells it how many
nested enclosing structures are to be broken out of. The default value
is 1, only the immediate enclosing structure is broken out of.
For example, the below code:
<?php
foreach(array(1,2,3,4,5) as $num){
foreach(array('a', 'b') as $char){
if($char === 'b') {
break;
}
echo $char;
}
echo $num;
}
// Result: a1a2a3a4a5
It breaks before it prints 'b', but continues with the loop to 5.
if i'm looping over an array, and while in the middle of one of the loops i discover some small issue, change ...something..., and need to try again ... is there a way to jump back to the top of the loop without grabbing the next value out of the array?
i doubt this exists, but it would be some keyword like continue or break. in fact, it would be a lot like continue, except that it doesn't get the next item, it maintains what it has in memory.
if nothing exists, can i insert something into the array in such a way that it will become the next key/value in the loop?
maybe this would be easier with a while(array_shift())...
or i suppose a recursive function inside the loop might work.
well, my question is evolving as i type this, so please review this pseudo code:
foreach($storage_locations as $storage_location) {
switch($storage_location){
case 'cookie':
if(headers_sent()) {
// cannot store in cookie, failover to session
// what can i do here to run the code in the next case?
// append 'session' to $storage_locations?
// that would make it run, but other items in the array would run first... how can i get it next?
} else {
set_cookie();
return;
}
break;
case 'session':
set_session();
return;
break;
}
}
i'm sure there is no keyword to change the value tested against in the switch mid-stream... so how should i refactor this code to get my failover?
Not with a foreach, but with more manual array iteration:
while (list($key, $value) = each($array)) {
if (...) {
reset($array); // start again
}
}
http://php.net/each
http://php.net/reset
It seems like a simple fall through would do the trick though:
switch ($storage_location) {
case 'cookie':
if (!headers_sent()) {
set_cookie();
break;
}
// falls through to next case
case 'session':
Why this is not possible to accomplish?
foreach($arr as $k => $v)
{
if($condition) { $obj->myMethod() && continue; }
}
After $obj->myMethod() gets evaluated then the keyword continue is evaluated (executed), resulting in skipping the current iteration.
EDIT: i'm asking this because something like:
if($error) { $log->fatal('Something weird happened.') && continue; }
is single line and self-explanatory.
continue is a statement not an expression.
And never the twain shall meet.
You can't put a statement in an expression. (What would echo false && continue; print?)
Instead, use an if, which can contain statements.
You cannot evaluate continue as a condition. The continue keyword does not work the same way as in other languages. In PHP, depending on context continue and break can be somewhat synonymous, consider this construct:
<?php
switch ($months)
{
// start with vowels
case 'august':
break;
case 'april':
continue; // exactly the same as "break" !!!
default:
return 'OK';
}
throw new StartsWithVowelException('Months with vowels are creepy');
?>
While we are on the topic, the break and continue keywords have a feature in PHP that make them a bit more interesting and powerful than their peers in other languages.
Both can be given a numerical argument when used in a loop that indicates how many loops to continue through or break out of. For example, here is an example that restarts the execution of an outer loop from within an inner one::
<?php
//
// verify that each sub array contains the given value
//
$lowerval = strtolower($value);
foreach ($TwoDArray as $otherArray)
{
foreach ($otherArray as $value)
{
if (strtolower($value) == $lowerval)
{
// we found the value -- this one definitely has it.
continue 2;
}
}
// if we've reached here, then the inner loop doesn't have the
// value. ¡aiiee!
}
?>
Hope this helps you out with these 2 constructs, good-luck.
continue is a statement. In PHP a statement and an expression are two different things, statements cannot be evaluated because they do not by nature return true or false which is a requirement for evaluation in PHP.
In PHP you'd have to do something like:
if(test()) continue;
I am getting the key from a given value in a multidimensional array. It works fine except that I cannot seem to access the variable from OUTSIDE the nested foreach loop that I'm using to get the key.
so my foreach loop is: ($name_books is the multi-d array which contains 3 smaller arrays)
foreach($name_books as $test) {
foreach ($test as $key => $value) {
$book_code = array_search($row['name'],$test);
echo $book_code; //just to see if it works, which it does
break;
}
}
//But then if I go outside of the loop..
echo $book_code." is the book code"; // <--DOES NOT WORK
So I know that I'm dealing with variable scope issues here and I've tried declaring globals inside the foreach loop but nothing works.
I'm sure there is something absurdly simple I'm missing!
EDIT:
urg..I took a step back and realized something else,
all this is happening inside a while loop (getting stuff from a db)
so the code is more like:
while($row=mysql_fetch_assoc($result)) {
...original foreach loop from above
}
apologies for not including this, I was focusing on this tiny piece and forgot to back up and see where it fit.
break;
Will only exit the internal nested foreach. If there are more rows in $name_books, it will continue looping and eventually overwriting $book_code with 'false' values from array_search;
Once you've found the value you're looking for, use:
break 2;
Regarding your edit, where you break depends on what you're doing with the value you've found for $book_code. If you don't plan on continuing, change the parameter for break. break 3; will exit the while loop too. Change the value depending on the level of nesting.
This has nothing to do with variable scope so long as what you posted is exactly what you have in your script.
I think what the problem is, is that you are only breaking out of the inner loop. In each iteration of the outer loop, $book_code will get changed, so you need to stop the outer loop as well. Try changing break; to break 2; and see if it fixes your problem. That causes it to break out of both the inner and the outer loop.
Edit: I think you can also simplify your code:
foreach ($name_books as $test) {
$book_code = array_search($row['name'], $test);
if ($book_code !== FALSE) {
break;
}
}If I knew more about your structure, this could possibly be reduced down to a single SQL statement and 0 loops.
simshaun is right, but I would actually take a different approach.
I would check for the existence of $book_code in my foreach loops rather than dealing with the breaks.
New Code
foreach($name_books as $test) {
foreach ($test as $key => $value) {
if(!isset($book_code)){
$book_code = array_search($row['name'],$test);
echo $book_code; //just to see if it works, which it does
}
}
}
echo $book_code." is the book code";