Declare a static variable in a foreach in PHP - php

i have a very simple question. How can i make this code
$i = 0;
foreach($Array as $Value)
{
echo $i;
$i++
}
but written like this?
foreach($Array as $Value)
{
$i = 0;
echo $i;
$i++
}
should i use a STATIC variable? or what? I don't have a clear view on this.
Thank you!

You shouldn't really do that. static variables are used to persist a variable's value between invocations of the function they're embedded in. They're not useful for a simple loop. Your second code will simply reset the counter to zero on every iteration.
e.g. this is a correct usage:
function count() {
static $x = 0; // executed the first time count() is called, then never again"
echo ++$x;
}
count(); // 1
count(); // 2
count(); // 3
You can certainly have
foreach($array as $val) {
static $x = 0;
echo ++$x;
}
but you don't gain anything, since that particular piece of code never goes out of scope for the duration of the loop, so $x's value would never get "lost".

you may want use
foreach($Array as $i => $Value)
{
echo $i;
}
or
foreach(array_values($Array) as $i=>$Value)
{
echo $i;
}
While first your example is correct, too

Related

PHP generator yield the first value, then iterate over the rest

I have this code:
<?php
function generator() {
yield 'First value';
for ($i = 1; $i <= 3; $i++) {
yield $i;
}
}
$gen = generator();
$first = $gen->current();
echo $first . '<br/>';
//$gen->next();
foreach ($gen as $value) {
echo $value . '<br/>';
}
This outputs:
First value
First value
1
2
3
I need the 'First value' to yielding only once. If i uncomment $gen->next() line, fatal error occured:
Fatal error: Uncaught exception 'Exception' with message 'Cannot rewind a generator that was already run'
How can I solve this?
The problem is that the foreach try to reset (rewind) the Generator. But rewind() throws an exception if the generator is currently after the first yield.
So you should avoid the foreach and use a while instead
$gen = generator();
$first = $gen->current();
echo $first . '<br/>';
$gen->next();
while ($gen->valid()) {
echo $gen->current() . '<br/>';
$gen->next();
}
chumkiu's answer is correct. Some additional ideas.
Proposal 0: remaining() decorator.
(This is the latest version I am adding here, but possibly the best)
PHP 7+:
function remaining(\Generator $generator) {
yield from $generator;
}
PHP 5.5+ < 7:
function remaining(\Generator $generator) {
for (; $generator->valid(); $generator->next()) {
yield $generator->current();
}
}
Usage (all PHP versions):
function foo() {
for ($i = 0; $i < 5; ++$i) {
yield $i;
}
}
$gen = foo();
if (!$gen->valid()) {
// Not even the first item exists.
return;
}
$first = $gen->current();
$gen->next();
$values = [];
foreach (remaining($gen) as $value) {
$values[] = $value;
}
There might be some indirection overhead. But semantically this is quite elegant I think.
Proposal 1: for() instead of while().
As a nice syntactic alternative, I propose using for() instead of while() to reduce clutter from the ->next() call and the initialization.
Simple version, without your initial value:
for ($gen = generator(); $gen->valid(); $gen->next()) {
echo $gen->current();
}
With the initial value:
$gen = generator();
if (!$gen->valid()) {
echo "Not even the first value exists.<br/>";
return;
}
$first = $gen->current();
echo $first . '<br/>';
$gen->next();
for (; $gen->valid(); $gen->next()) {
echo $gen->current() . '<br/>';
}
You could put the first $gen->next() into the for() statement, but I don't think this would add much readability.
A little benchmark I did locally (with PHP 5.6) showed that this version with for() or while() with explicit calls to ->next(), current() etc are slower than the implicit version with foreach(generator() as $value).
Proposal 2: Offset parameter in the generator() function
This only works if you have control over the generator function.
function generator($offset = 0) {
if ($offset <= 0) {
yield 'First value';
$offset = 1;
}
for ($i = $offset; $i <= 3; $i++) {
yield $i;
}
}
foreach (generator() as $firstValue) {
print "First: " . $firstValue . "\n";
break;
}
foreach (generator(1) as value) {
print $value . "\n";
}
This would mean that any initialization would run twice. Maybe not desirable.
Also it allows calls like generator(9999) with really high skip numbers. E.g. someone could use this to process the generator sequence in chunks. But starting from 0 each time and then skipping a huge number of items seems really a bad idea performance-wise. E.g. if the data is coming from a file, and skipping means to read + ignore the first 9999 lines of the file.
solutions provided here does not work if you need to iterate more than once.
so I used iterator_to_array function to convert it to array;
$items = iterator_to_array($items);

Go back at the beginning of a foreach loop in PHP

Is it possible ? Or should I end the loop and beginning another ?
foreach($array as $i)
{
if (something)
// Go back
}
It is. But not with foreach & without leaving the loop.
Here's another alternative, for good measure.
for ($i = 0; $i < count($array); $i++) {
if (condition) {
$i = 0;
}
do_stuff_with($array[$i]);
}
It is not suggested but you can use goto:
cIterator: {
foreach($array as $i)
{
if (something)
goto cIterator;
}
}
Create a function and pass the array. If something happens in the loop call the function again with the main array. Try this -
function check_loop($array) {
foreach($array as $val) {
if (something)
check_loop($array);
}
}
check_loop($array);
You can use current(), next() and prev() to loop through array and move the internal array pointer back and forth:
$items = array("apple", "box", "cat");
while($item=current($items)) {
print_r($item);
if (needToGoBack($item))
// Go to previous array item
$item = reset($items);
} else {
// Continue to next
$item = next($items);
}
}
Use continue
From the PHP docs: continue is used within looping structures to skip the rest of the current loop iteration and continue execution at the condition evaluation and then the beginning of the next iteration.
http://php.net/manual/en/control-structures.continue.php

Multiple returning

Could anyone help me.
I need to return multiple img's, but with this code, only one of two is returning.
What is the solution.
Thank you in advance.
$test = "/claim/img/box.png, /claim/img/box.png";
function test($test)
{
$photo = explode(',', $test);
for ($i = 0; $i < count($photo); $i++)
{
$returnas = "<img src=".$photo[$i].">";
return $returnas;
}
}
This might be a good opportunity to learn about array_map.
function test($test) {
return implode("",array_map(function($img) {
return "<img src='".trim($img)."' />";
},explode(",",$test)));
}
Many functions make writing code a lot simpler, and it's also faster because it uses lower-level code.
While we're on the subject of learning things, PHP 5.5 gives us generators. You could potentially use one here. For example:
function test($test) {
$pieces = explode(",",$test);
foreach($pieces as $img) {
yield "<img src='".trim($img)."' />";
}
}
That yield is where the magic happens. This makes your function behave like a generator. You can then do this:
$images = test($test);
foreach($images as $image) echo $image;
Personally, I think this generator solution is a lot cleaner than the array_map one I gave earlier, which in turn is tidier than manually iterating.
Modify your code that way
function test($test)
{
$returnas = '';
$photo = explode(',', $test);
for ($i = 0; $i < count($photo); $i++)
{
$returnas .= "<img src=".$photo[$i].">";
}
return $returnas;
}
Your code didn't work since you were returning inside the loop immediatly. Every programming language support "only a return for call". In my solution you're appendig a string that has an img tag each time you enter the loop and return it after every photo is "passed" into the loop
You could even use the foreach() construct, of course
Bonus answer
If you don't know the difference between ...
for ($i = 0; $i < count($photo); $i++)
and
for ($i = 0, $count = count($photo); $i < $<; $i++)
Well, in first case you'll evaluate count($photo) every single time the for is called whereas the second time, it is evaluated only once.
This could be used for optimization porpuses (even if php, internally, stores the length of an array so it is accesible in O(1))
The function breaks after the first return statement. You need to save what you want to return in some structure, an array eg, and return this.
function test($test)
{
$result = array();
$photo = explode(',', $test);
for ($i = 0; $i < count($photo); $i++)
{
$returnas = "<img src=".$photo[$i].">";
$result[] = $returnas;
}
return $result;
}

Php while loop not working, Strange error

Here is my php:
<?
$i = 0;
function f() {
$i++;
echo $i;
if ($i < 3) {
return true;
}
}
while(f())
?>
I was expecting output to be 123
But I get this:
Fatal error: Maximum execution time of 30 seconds exceeded in exp.php on line 5
$i is not defined in function's scope. Everytime it resets to zero.
$i = 0;
function f() {
global $i;
$i++;
echo $i;
return $i<3; //thanks #styxxy
}
while (f());
The $i variable inside your function is a local variable (of that function). If you want to access the variable(s) outside the function, use global. This has to do with the variable scope.
<?php
$i = 0;
function f() {
global $i;
$i++;
echo $i;
if ($i < 3) return true;
return false;
}
while(f());
It is also good practice to make sure you return a value on all code paths (and not rely on the the defaults).
$i is a global variable, declared outside of the function. The other $i is a local variable, it has a storage location but no initial value. If you want to refer to the global variable from within the function, add the global keyword...
i is a global variable right?
i think you should make it known to the function f().
Like this
global $i;
Why not just:
$i = 0;
while($i<3){
$i++;
f($i);
}
function f($i){
echo $i;
}

Knowning at which point we are in a foreach

I have an array:
$array=array('key1'=>'value1','key2'=>'value2','value3');
and a foreach:
foreach($array as $v){
//do something
}
Is there a way to know in the foreach which element we are parsing?
(without doing something like:)
$count=0;
foreach($array as $v){
$count++;
// do something
}
thanks
EDIT1:
Sorry maybe I was not clear:
I don't want know the key, but I need to know how many elements are left in the foreach. (that's why I did the example with $count)
You could use a class that extends ArrayIterator:
class FooArrayIterator extends ArrayIterator {
private $offset = 0;
public function next() {
$this->offset++;
return parent::next();
}
public function rewind() {
$this->offset = 0;
parent::rewind();
}
public function seek($offset) {
if ($offset >= 0 && $offset < $this->count()) {
$this->offset = $offset;
}
parent::seek($offset);
}
public function offset() {
return $this->offset;
}
}
Example:
$array = array('value1','value2','value3');
$iter = new FooArrayIterator($array);
foreach ($iter as $v) {
echo ($iter->count() - $iter->offset() - 1).' more';
}
You can get the actual index:
foreach ($array as $index => $value) {
}
If you are working with an associative array there is no way to tell the current position of the internal array pointer. There is only an indirect way: you search for the current key in the keys of the array with:
foreach ($array as $index => $value) {
$position = array_search ($index, array_keys ($array));
echo ($position);
}
... but I guess count++ is a much more resource-friendly way.
You can:
$count = count($array);
foreach($array as $key => $value) {
$count--;
//$count elements are left
}
Yes, sort of.
foreach($array as $key=>$value)
// code
}
$key will be the array key, although if you want an actual integer count of iterations, and the keys are not numbered sequentially, or are strings, you will have to use a counter like in your original post.
Edit: to handle the last element without implementing a counter, you can use (if keys are int)
$foo = ($key == count($array)-1) ? "last element" : "any other element";
(janked from the manual comments - http://php.net/manual/en/control-structures.foreach.php)
Edit: if your keys are not integers, you can create a counter like you have in your code above, and substitute $key with your counter variable.
You're being a bit too picky :)
By the way the trick is to transform an associative array to an indexed array:
foreach ( array_values($array) as $key=>$value ){
echo $key; //yes, it will be an INT
echo ( count($array) - $key );
}
Without some pre-processing, such as your count() version, there isn't any way to know where you are in an associative array. At most you can check if you're at the end with end(), but there's no guarantee as to what order foreach() will retrieve the individual elements. Generally it's the same order they got added to the array, but not guarantees.
Another pre-processing option would be
$keys = array_keys($array);
$cnt = count($keys)
for ($i = 0; $i < $cnt; $i++) {
$element = $array[$keys[$i]];
}
and $i would tell you exactly how far through you've gone.

Categories