web-dev-qa-db-fra.com

Obtenir le prochain élément dans la boucle foreach

J'ai une boucle foreach et je veux voir s'il y a un élément suivant dans la boucle afin de pouvoir comparer l'élément en cours avec le suivant. Comment puis-je faire ceci? J'ai lu des informations sur les fonctions actuelles et suivantes, mais je ne sais pas comment les utiliser.

Merci d'avance

41
chchrist

Une approche unique consisterait à inverser le tableau et puis boucle. Cela fonctionnera également pour les tableaux non indexés numériquement:

$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 vous souhaitez toujours utiliser les fonctions current et next, vous pouvez procéder comme suit:

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

# 2 est probablement la meilleure solution. Notez que $i < $length - 1; arrêtera la boucle après avoir comparé les deux derniers éléments du tableau. Je mets cela dans la boucle pour être explicite avec l'exemple. Vous devriez probablement simplement calculer $length = count($items) - 1;

29
Stephen

Vous pourriez probablement utiliser la boucle while au lieu de foreach:

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

Comme php.net/foreach fait remarquer:

À moins que le tableau ne soit référencé, foreach agit sur une copie du tableau spécifié et non sur le tableau lui-même. foreach a des effets secondaires sur le pointeur du tableau. Ne comptez pas sur le pointeur de tableau pendant ou après le foreach sans le réinitialiser.

En d'autres termes, ce n'est pas une très bonne idée de faire ce que vous demandez de faire. Peut-être que ce serait une bonne idée de parler à quelqu'un de la raison pour laquelle vous essayez de faire cela, voir s'il existe une meilleure solution? N'hésitez pas à nous demander en ## PHP sur irc.freenode.net si vous n'avez aucune autre ressource disponible.

10
TML

Si les index sont continus:

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

si son index numérique:

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

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

La solution générale pourrait être un itérateur de cache. Un itérateur de mise en cache correctement implémenté fonctionne avec n'importe quel Iterator et économise de la mémoire. PHP SPL a un CachingIterator , mais il est très étrange et ses fonctionnalités sont très limitées. Cependant, vous pouvez écrire votre propre itérateur lookahead comme ceci:

<?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";
    }
}
3
Dávid Horváth

Vous pouvez obtenir les clés du tableau avant foreach, puis utiliser un compteur pour vérifier l'élément suivant, quelque chose comme:

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

Cela fonctionnera pour les tableaux simples, tels que array(1,2,3), et les tableaux à clé tels que array('first'=>1, 'second'=>2, 'thrid'=>3).

2
eclipse31

Une boucle foreach en php va parcourir une copie du tableau d'origine, rendant les fonctions next() et prev() inutiles. Si vous avez un tableau associatif et devez récupérer l'élément suivant, vous pouvez plutôt effectuer une itération sur les clés du tableau:

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

Étant donné que le tableau de clés résultant a lui-même un index continu, vous pouvez l'utiliser à la place pour accéder au tableau d'origine.

Soyez conscient que $next sera null pour la dernière itération, car il n'y a aucun élément suivant après la dernière. L'accès à des clés de tableau non existantes jettera un avis php. Pour éviter cela, soit:

  1. Vérifiez la dernière itération avant d'attribuer des valeurs à $next
  2. Vérifier si la clé avec index + 1 existe avec array_key_exists()

En utilisant la méthode 2, le foreach complet pourrait ressembler à ceci:

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
Liquinaut

Vous pouvez obtenir les clés/valeurs et 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";
}

résultat:

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
Andrei Krasutski