Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

nested foreach with iterator interface

<? foreach ($this->criteria as $key => $value): ?>
<li><?= $this->accommodationsLink($this->criteria, $key) ?></li>
<? endforeach ?>

This code give unexpected results, because only one link is visible. But there are two items in $this->criteria.

I explored the cause of the probleem. In the function accommodationsLink is another foreach loop that works on the same criteria object

foreach ($criteria as $key => $value) {
    $params[$key] = $value;
}

$this->criteria and $criteria are the same object that implements the php Iterator interface. Is there a simple way to let this code work or are nested foreach loops not possible with php iterator interface?

like image 288
Derk Avatar asked Aug 04 '10 12:08

Derk


1 Answers

Well, the second foreach is going to call $iterator->reset() prior to running. So when the second foreach reaches the end of the iterator, the internal pointer is already at the end of the array...

It'd be like:

$it->reset();
while ($it->valid()) {
   $it->reset();
   while ($it->valid()) {
       //do something
       $it->next();
   }
   $it->next();
}

Buy the time it gets to the $it->next() call in the outer loop, it's already invalid. So the next() call will "fail", and $it->valid() would return false.

It's not a problem with iterators, it's a problem with the logic you're using. If you really must nest loops, then clone the iterator ($subit = clone $it) in the inner loop so you don't disturb the pointer...

Edit: Example with cloning:

$it->reset();
while ($it->valid()) {
   $bar = clone $it;
   $bar->reset();
   while ($bar->valid()) {
       //do something
       $bar->next();
   }
   $it->next();
}

Or, using foreach (which is semantically equivalent):

foreach ($it as $key => $value) {
    $subit = clone $it;
    foreach ($subit as $k => $v) {
        //Do stuff
    }
}
like image 94
ircmaxell Avatar answered Oct 24 '22 22:10

ircmaxell