Shobi
Shobi

Reputation: 11481

SPL InfiniteIterator is not working with next or prev

I wanted to over an array infinitely like a circular array.

I have the following setup using InfiniteIterator1, 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

Answers (3)

Shobi
Shobi

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

sevavietl
sevavietl

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

Nigel Ren
Nigel Ren

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

Related Questions