Obtener el siguiente elemento en el bucle foreach


Tengo un bucle foreach y quiero ver si hay un elemento siguiente en el bucle para poder comparar el elemento actual con el siguiente. ¿Cómo puedo hacer esto? He leído sobre las funciones actual y siguiente, pero no puedo averiguar cómo usarlos.

Gracias de antemano

 39
Author: chchrist, 2011-02-23

9 answers

Un enfoque único sería invertir la matriz y luego bucle. Esto también funcionará para matrices no indexadas numéricamente:

$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;
}

Si todavía estás interesado en usar las funciones current y next, puedes hacer esto:

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

#2 es probablemente la mejor solución. Nota, $i < $length - 1; detendrá el bucle después de comparar los dos últimos elementos de la matriz. Puse esto en el bucle para ser explícito con el ejemplo. Probablemente deberías calcular $length = count($items) - 1;

 27
Author: Stephen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-23 21:03:58

Como php.net/foreach señala:

A menos que se haga referencia al array, foreach opera en una copia del array especificado y no en el array en sí. foreach tiene algunos efectos secundarios en el puntero de matriz. No confíe en el puntero de matriz durante o después del foreach sin restablecerlo.

En otras palabras - no es una muy buena idea hacer lo que estás pidiendo hacer. Tal vez sería una buena idea hablar con alguien sobre por qué estás tratando de hacer esto, ver si ¿hay una solución mejor? Siéntase libre de preguntarnos en # # PHP en irc.freenode.net si no tiene otros recursos disponibles.

 9
Author: TML,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-24 08:05:28

Probablemente podrías usar bucle while en lugar de foreach:

while ($current = current($array) )
{
    $next = next($array);
    if (false !== $next && $next == $current)
    {
        //do something with $current
    }
}
 9
Author: pronskiy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-12 07:27:19

Si los índices son continuos:

foreach ($arr as $key => $val) {
   if (isset($arr[$key+1])) {
      echo $arr[$key+1]; // next element
   } else {
     // end of array reached
   }
}
 6
Author: Mārtiņš Briedis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-06 01:06:49

Si está indexado numéricamente:

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

    if($var==$foo[$key+1]){
        echo 'current and next var are the same';
    }
}
 4
Author: Rob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-14 20:39:00

Puede obtener las claves del array antes del foreach, luego usar un contador para verificar el siguiente elemento, algo así como:

//$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++;
}

Esto funcionará tanto para matrices simples, como array(1,2,3), como para matrices con claves como array('first'=>1, 'second'=>2, 'thrid'=>3).

 2
Author: eclipse31,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-24 09:36:18

La solución general podría ser un iterador de almacenamiento en caché. Un iterador de almacenamiento en caché correctamente implementado funciona con cualquier iterador y ahorra memoria. PHP SPL tiene un CachingIterator, pero es muy extraño, y tiene una funcionalidad muy limitada. Sin embargo, puedes escribir tu propio iterador lookahead así:

<?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";
    }
}
 2
Author: Dávid Horváth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-14 19:50:52

Un bucle foreach en php iterará sobre una copia del array original, haciendo inútiles las funciones next() y prev(). Si tiene una matriz asociativa y necesita obtener el siguiente elemento, podría iterar sobre las claves de la matriz en su lugar:

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]];
}

Dado que el array de claves resultante tiene un índice continuo en sí mismo, puede usarlo en su lugar para acceder al array original.

Tenga en cuenta que $next será null para la última iteración, ya que no hay siguiente elemento después de la última. Acceder a claves de array no existentes lanzará un aviso de php. Para evitar eso, ya sea:

  1. Compruebe la última iteración antes de asignar valores a $next
  2. Compruebe si la clave con index + 1 existe con array_key_exists()

Usando el método 2 el foreach completo podría verse así:

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]];
    }
}
 0
Author: Liquinaut,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-14 16:39:24

Puede obtener las claves / valores y el índice

<?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";
}

Resultado:

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 = ( => )
 0
Author: Andrei Krasutski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-23 15:17:12