chchrist
chchrist

Reputation: 19812

Get next element in foreach loop

I have a foreach loop and I want to see if there is a next element in the loop so I can compare the current element with the next. How can I do this? I've read about the current and next functions but I can't figure out how to use them.

Upvotes: 69

Views: 165885

Answers (12)

Abolfazl Sharifi
Abolfazl Sharifi

Reputation: 359

You can simply save array keys in a variable:

$arrayKeys = array_keys($array);

and find by that in your foreach loop:

foreach ($array as $key => $value ) {
    $nextKey = $arrayKeys[array_search($key, $arrayKeys) + 1];
}

Upvotes: -1

abe1432181
abe1432181

Reputation: 1316

or if the array is associative then you could use current() similar to Andrei Krasutski's solution and key()

$values = [];
array_push($values, ["XYZ"=>100]);
array_push($values, ["ABC"=>10]);
array_push($values, ["XYZ"=>130]);
array_push($values, ["DEF"=>4]);
array_push($values, ["XYZ"=>5]);

$count = count($values);

foreach ($values as $index => $currentValue) {

    if ($index < $count - 1) {

        $nextValue = $values[$index + 1];
        echo key($currentValue) . "=" . current($currentValue) . " followed by  " . key($nextValue) . "/" . current($nextValue) . "<br>\n";

    } else {

        echo key($currentValue) . "=" . current($currentValue);

    }
}

See https://onlinephp.io/c/dc58d for a running example.

or if the array is using named pairs:

$values = [];
array_push($values, ["type"=>"XYZ", "value"=>100]);
array_push($values, ["type"=>"ABC", "value"=>10]);
array_push($values, ["type"=>"XYZ", "value"=>130]);
array_push($values, ["type"=>"DEF", "value"=>"Lorem"]);
array_push($values, ["type"=>"XYZ", "value"=>5]);

$count = count($values);

foreach ($values as $index => $currentValue) {
    if ($index < $count - 1) {
        $nextValue = $values[$index + 1];
        echo $currentValue['type'] . "=" . $currentValue['value'] 
        . "  followed by  " . $nextValue['type'] . "/" . $nextValue['value'] . "<br>\n";
    } else {
        echo $currentValue['type'] . "=" . $currentValue['value'];
    }
}

Upvotes: 0

SirCumz
SirCumz

Reputation: 171

    $next_data = $data;

    $prev_key = null;
    $prev_value = null;

    foreach($data as $key => $value)
    {
        array_shift($next_data);

        $next_key = key($next_data);
        $next_value = $next_data[$next_key] ?? null;

        // Do something here...

        $prev_key = $key;
        $prev_value = $value;
    }

Upvotes: 0

Mārtiņš Briedis
Mārtiņš Briedis

Reputation: 17762

If the indexes are continuous:

foreach ($arr as $key => $val) {
   if (isset($arr[$key+1])) {
      echo $arr[$key+1]; // next element
   } else {
     // end of array reached
   }
}

Upvotes: 16

Andrei Krasutski
Andrei Krasutski

Reputation: 5257

You could get the keys/values and index

<?php
$a = array(
    'key1'=>'value1', 
    'key2'=>'value2', 
    'key3'=>'value3', 
    'key4'=>'value4', 
    'key5'=>'value5'
);

$keys = array_keys($a);
foreach(array_keys($keys) as $index ){       
    $current_key = current($keys); // or $current_key = $keys[$index];
    $current_value = $a[$current_key]; // or $current_value = $a[$keys[$index]];

    $next_key = next($keys); 
    $next_value = $a[$next_key] ?? null; // for php version >= 7.0

    echo  "{$index}: current = ({$current_key} => {$current_value}); next = ({$next_key} => {$next_value})\n";
}

result:

0: current = (key1 => value1); next = (key2 => value2) 
1: current = (key2 => value2); next = (key3 => value3) 
2: current = (key3 => value3); next = (key4 => value4) 
3: current = (key4 => value4); next = (key5 => value5) 
4: current = (key5 => value5); next = ( => )

Upvotes: 8

D&#225;vid Horv&#225;th
D&#225;vid Horv&#225;th

Reputation: 4320

The general solution could be a caching iterator. A properly implemented caching iterator works with any Iterator, and saves memory. PHP SPL has a CachingIterator, but it is very odd, and has very limited functionality. However, you can write your own lookahead iterator like this:

<?php

class NeighborIterator implements Iterator
{

    protected $oInnerIterator;

    protected $hasPrevious = false;
    protected $previous = null;
    protected $previousKey = null;

    protected $hasCurrent = false;
    protected $current = null;
    protected $currentKey = null;

    protected $hasNext = false;
    protected $next = null;
    protected $nextKey = null;

    public function __construct(Iterator $oInnerIterator)
    {
        $this->oInnerIterator = $oInnerIterator;
    }

    public function current()
    {
        return $this->current;
    }

    public function key()
    {
        return $this->currentKey;
    }

    public function next()
    {
        if ($this->hasCurrent) {
            $this->hasPrevious = true;
            $this->previous = $this->current;
            $this->previousKey = $this->currentKey;
            $this->hasCurrent = $this->hasNext;
            $this->current = $this->next;
            $this->currentKey = $this->nextKey;
            if ($this->hasNext) {
                $this->oInnerIterator->next();
                $this->hasNext = $this->oInnerIterator->valid();
                if ($this->hasNext) {
                    $this->next = $this->oInnerIterator->current();
                    $this->nextKey = $this->oInnerIterator->key();
                } else {
                    $this->next = null;
                    $this->nextKey = null;
                }
            }
        }
    }

    public function rewind()
    {
        $this->hasPrevious = false;
        $this->previous = null;
        $this->previousKey = null;
        $this->oInnerIterator->rewind();
        $this->hasCurrent = $this->oInnerIterator->valid();
        if ($this->hasCurrent) {
            $this->current = $this->oInnerIterator->current();
            $this->currentKey = $this->oInnerIterator->key();
            $this->oInnerIterator->next();
            $this->hasNext = $this->oInnerIterator->valid();
            if ($this->hasNext) {
                $this->next = $this->oInnerIterator->current();
                $this->nextKey = $this->oInnerIterator->key();
            } else {
                $this->next = null;
                $this->nextKey = null;
            }
        } else {
            $this->current = null;
            $this->currentKey = null;
            $this->hasNext = false;
            $this->next = null;
            $this->nextKey = null;
        }
    }

    public function valid()
    {
        return $this->hasCurrent;
    }

    public function hasNext()
    {
        return $this->hasNext;
    }

    public function getNext()
    {
        return $this->next;
    }

    public function getNextKey()
    {
        return $this->nextKey;
    }

    public function hasPrevious()
    {
        return $this->hasPrevious;
    }

    public function getPrevious()
    {
        return $this->previous;
    }

    public function getPreviousKey()
    {
        return $this->previousKey;
    }

}


header("Content-type: text/plain; charset=utf-8");
$arr = [
    "a" => "alma",
    "b" => "banan",
    "c" => "cseresznye",
    "d" => "dio",
    "e" => "eper",
];
$oNeighborIterator = new NeighborIterator(new ArrayIterator($arr));
foreach ($oNeighborIterator as $key => $value) {

    // you can get previous and next values:

    if (!$oNeighborIterator->hasPrevious()) {
        echo "{FIRST}\n";
    }
    echo $oNeighborIterator->getPreviousKey() . " => " . $oNeighborIterator->getPrevious() . " ----->        ";
    echo "[ " . $key . " => " . $value . " ]        -----> ";
    echo $oNeighborIterator->getNextKey() . " => " . $oNeighborIterator->getNext() . "\n";
    if (!$oNeighborIterator->hasNext()) {
        echo "{LAST}\n";
    }
}

Upvotes: 4

Liquinaut
Liquinaut

Reputation: 3807

A foreach loop in php will iterate over a copy of the original array, making next() and prev() functions useless. If you have an associative array and need to fetch the next item, you could iterate over the array keys instead:

foreach (array_keys($items) as $index => $key) {
    // first, get current item
    $item = $items[$key];
    // now get next item in array
    $next = $items[array_keys($items)[$index + 1]];
}

Since the resulting array of keys has a continuous index itself, you can use that instead to access the original array.

Be aware that $next will be null for the last iteration, since there is no next item after the last. Accessing non existent array keys will throw a php notice. To avoid that, either:

  1. Check for the last iteration before assigning values to $next
  2. Check if the key with index + 1 exists with array_key_exists()

Using method 2 the complete foreach could look like this:

foreach (array_keys($items) as $index => $key) {
    // first, get current item
    $item = $items[$key];
    // now get next item in array
    $next = null;
    if (array_key_exists($index + 1, array_keys($items))) {
        $next = $items[array_keys($items)[$index + 1]];
    }
}

Upvotes: 1

pronskiy
pronskiy

Reputation: 1358

You could probably use while loop instead of foreach:

while ($current = current($array) )
{
    $next = next($array);
    if (false !== $next && $next == $current)
    {
        //do something with $current
    }
}

Upvotes: 31

user557846
user557846

Reputation:

if its numerically indexed:

foreach ($foo as $key=>$var){

    if($var==$foo[$key+1]){
        echo 'current and next var are the same';
    }
}

Upvotes: 3

eclipse31
eclipse31

Reputation: 396

You could get the keys of the array before the foreach, then use a counter to check the next element, something like:

//$arr is the array you wish to cycle through
$keys = array_keys($arr);
$num_keys = count($keys);
$i = 1;
foreach ($arr as $a)
{
    if ($i < $num_keys && $arr[$keys[$i]] == $a)
    {
        // we have a match
    }
    $i++;
}

This will work for both simple arrays, such as array(1,2,3), and keyed arrays such as array('first'=>1, 'second'=>2, 'thrid'=>3).

Upvotes: 3

TML
TML

Reputation: 12966

As php.net/foreach points out:

Unless the array is referenced, foreach operates on a copy of the specified array and not the array itself. foreach has some side effects on the array pointer. Don't rely on the array pointer during or after the foreach without resetting it.

In other words - it's not a very good idea to do what you're asking to do. Perhaps it would be a good idea to talk with someone about why you're trying to do this, see if there's a better solution? Feel free to ask us in ##PHP on irc.freenode.net if you don't have any other resources available.

Upvotes: 13

Stephen
Stephen

Reputation: 18964

A unique approach would be to reverse the array and then loop. This will work for non-numerically indexed arrays as well:

$items = array(
    'one'   => 'two',
    'two'   => 'two',
    'three' => 'three'
);
$backwards = array_reverse($items);
$last_item = NULL;

foreach ($backwards as $current_item) {
    if ($last_item === $current_item) {
        // they match
    }
    $last_item = $current_item;
}

If you are still interested in using the current and next functions, you could do this:

$items = array('two', 'two', 'three');
$length = count($items);
for($i = 0; $i < $length - 1; ++$i) {
    if (current($items) === next($items)) {
        // they match
    }
}

#2 is probably the best solution. Note, $i < $length - 1; will stop the loop after comparing the last two items in the array. I put this in the loop to be explicit with the example. You should probably just calculate $length = count($items) - 1;

Upvotes: 47

Related Questions