Reputation: 11481
I wanted to over an array infinitely like a circular array.
I have the following setup using InfiniteIterator
1, which iterates over the $players
infinitely. But actually, I want to know the next player and the previous player from this loop like below
$players = new InfiniteIterator(new ArrayIterator(['Shobi', 'Jomit']));
foreach ($players as $player) {
echo $player; // current player name comes properly infinetly
echo next($players); // next player should come
echo current($players); // current player should come
echo prev($players); //previous player should come
}
but next()
and prev()
always return null
From the doc, I can see those methods are returning void, but is there any way I can extend the InfiniteIterator and achieve this mechanism required?
How do I make next()
and prev()
work with InfiniteIterator?
Edit current()
returns current item, (meaning, it works properly in the logic)
Upvotes: 1
Views: 223
Reputation: 11481
Posting an answer which I finally came up with.
The infinite iterator was not really working for my case. Especially because I was not able to move the pointer back using the prev()
function. So I did implement the Iterator Interface and override the next
and prev
methods accordingly.
So that when next()
is called and if it is at the end of the array, it will rewind the pointer to the beginning, likewise, if prev()
is called and the pointer is already at the beginning, it will move the pointer to the end of the internal array. This worked for me perfectly.
Relevant code
<?php
class CircularIterator implements Iterator
{
...
...
public function next()
{
next($this->entries);
if (!$this->current()) {
$this->rewind();
}
}
public function prev()
{
prev($this->entries);
if (!$this->current()) {
$this->end();
}
}
....
....
}
full implementation and sample code - Link
Upvotes: 0
Reputation: 3812
You can go nuts with PHP iterators and do something like the following:
$array = ['Shobi', 'Jomit', 'John', 'Jane', 'Smith'];
// Use NoRewindIterator to prevent MultipleIterator rewinding.
$players1 = new NoRewindIterator(new InfiniteIterator(new ArrayIterator($array)));
// Go to the end of array, i.e. set prev player.
for ($i = 0, $size = count($array); $i < $size - 1; $i++) {
$players1->next();
}
$players2 = new InfiniteIterator(new ArrayIterator($array));
$players2->next();
// Use NoRewindIterator to prevent MultipleIterator rewinding.
$players3 = new NoRewindIterator(new InfiniteIterator(new ArrayIterator($array)));
$players3->next(); // Go to the second player, i.e. next player
// MultipleIterator will traverse three iterators at once.
// Since the pointer in each iterator differs in one position, we will have prev, curr and next.
$players = new MultipleIterator(MultipleIterator::MIT_NEED_ALL|MultipleIterator::MIT_KEYS_ASSOC);
$players->attachIterator($players1, 'prev');
$players->attachIterator($players2, 'curr');
$players->attachIterator($players3, 'next');
$i = 0;
foreach ($players as $player) {
print_r($player);
if (++$i >= 10) {
break;
}
}
Please, see the demo.
Upvotes: 1
Reputation: 57131
If instead of using iterators, you can just use an array and a pointer to the 'current' entry and then use a while(true)
loop which will just keep going (you can always add a break
to stop it for testing or some condition). The various parts of the logic check if the current player is the last one - so the next one is the start one -
or if it's the first item so the previous is the end item. Also the increment resets once it gets to the end and starts over again...
$players = ['Shobi', 'Jomit'];
$playerKey = 0;
$playerCount = count($players);
while(true) {
echo $players[($playerKey+1)%$playerCount].PHP_EOL; // next player
echo $players[$playerKey].PHP_EOL; // current player
echo $players[($playerKey>0)?$playerKey-1:$playerCount-1].PHP_EOL; //previous player
$playerKey = ( $playerKey+1 == $playerCount )?0:$playerKey+1;
}
Upvotes: 1