Wie bestimme ich die erste und letzte Iteration in einer foreach-Schleife?

494

Die Frage ist einfach. Ich habe eine foreachSchleife in meinem Code:

foreach($array as $element) {
    //code
}

In dieser Schleife möchte ich anders reagieren, wenn wir uns in der ersten oder letzten Iteration befinden.

Wie macht man das?

mehdi
quelle

Antworten:

426

Sie könnten einen Zähler verwenden:

$i = 0;
$len = count($array);
foreach ($array as $item) {
    if ($i == 0) {
        // first
    } else if ($i == $len - 1) {
        // last
    }
    // …
    $i++;
}
Gumbo
quelle
24
Ich denke nicht, dass hier ein Downvoting stattfinden sollte, da dies auch richtig funktioniert und immer noch nicht so blöd ist wie das Verwenden von array_shiftund array_pop. Obwohl dies die Lösung ist, die ich gefunden hätte, wenn ich so etwas implementieren müsste, würde ich mich jetzt an die Antwort von Rok Kralj halten .
Shadyyx
15
Wenn ich einen Zähler benötige, bevorzuge ich die FOR-Schleife anstelle von FOREACH.
Rkawano
10
Wenn Sie verwenden $i = 1, müssen Sie sich keine Sorgen machen $len - 1, verwenden Sie einfach $len.
Aksu
2
@Twan Wie ist Punkt 3 richtig? Oder überhaupt relevant für diese Frage, da es sich um HTML handelt? Dies ist eindeutig eine PHP-Frage ... und wenn es um Markup-Semantik geht, sind es viel tiefere Fakten als "ein richtiger Blahblah ist immer besser als Blahblah (dies ist nicht einmal meine Meinung, es ist reine Tatsache)".
Deji
1
@rkawano, aber Sie können den benannten Schlüssel nicht erhalten, wenn Sie FOR-Schleife verwenden
Fahmi
1015

Wenn Sie eine Lösung bevorzugen, bei der der Zähler außerhalb der Schleife nicht initialisiert werden muss, schlage ich vor, den aktuellen Iterationsschlüssel mit der Funktion zu vergleichen, die Ihnen den letzten / ersten Schlüssel des Arrays angibt.

Dies wird mit dem kommenden PHP 7.3 etwas effizienter (und lesbarer).

Lösung für PHP 7.3 und höher:

foreach($array as $key => $element) {
    if ($key === array_key_first($array))
        echo 'FIRST ELEMENT!';

    if ($key === array_key_last($array))
        echo 'LAST ELEMENT!';
}

Lösung für alle PHP-Versionen:

foreach($array as $key => $element) {
    reset($array);
    if ($key === key($array))
        echo 'FIRST ELEMENT!';

    end($array);
    if ($key === key($array))
        echo 'LAST ELEMENT!';
}
Rok Kralj
quelle
44
Fantastische Antwort! Viel sauberer als ein paar Arrays.
Paul J
14
Dies sollte ganz nach oben sprudeln, da es die richtige Antwort ist. Ein weiterer Vorteil dieser Funktionen gegenüber der Verwendung von array_shift und array_pop besteht darin, dass die Arrays intakt bleiben, falls sie zu einem späteren Zeitpunkt benötigt werden. +1 für den Wissensaustausch.
Awemo
13
Dies ist definitiv der beste Weg, wenn Sie Ihren Code sauber halten möchten. Ich wollte es gerade verbessern, aber jetzt bin ich nicht davon überzeugt, dass sich der funktionale Aufwand dieser Array-Methoden lohnt. Wenn wir nur über das letzte Element sprechen, dann ist das end()+ key()über jede Iteration der Schleife - wenn es beides ist, werden jedes Mal 4 Methoden aufgerufen. Zugegeben, das wären sehr einfache Operationen und wahrscheinlich nur Zeigersuchen, aber dann geben die Dokumente dies an reset()und end() ändern den internen Zeiger des Arrays - ist es also schneller als ein Zähler? möglicherweise nicht.
Pospi
19
Ich denke nicht, dass Sie einen Reset ($ array) in einem foreach ausgeben sollten. Aus der offiziellen Dokumentation (www.php.net/foreach): "Da foreach auf den internen Array-Zeiger angewiesen ist, kann das Ändern innerhalb der Schleife zu unerwartetem Verhalten führen." Und Reset macht genau das (www.php.net/reset): "Setzen Sie den internen Zeiger eines Arrays auf sein erstes Element"
Gonçalo Queirós
11
@ GonçaloQueirós: Es funktioniert. Foreach arbeitet an einer Kopie des Arrays. Wenn Sie dennoch besorgt sind, können Sie den reset()Anruf vor dem foreach verschieben und das Ergebnis zwischenspeichern $first.
Rok Kralj
121

Um den letzten Artikel zu finden, funktioniert dieser Code jedes Mal:

foreach( $items as $item ) {
    if( !next( $items ) ) {
        echo 'Last Item';
    }
}
Yojance
quelle
2
Dies hat zu wenig positive Stimmen. Gibt es einen Nachteil bei der Verwendung?
Kevin Kuyl
2
@ Kevin Kuyl - Wie oben von Pang erwähnt, führt diese Methode zu unerwarteten Ergebnissen, wenn das Array ein Element enthält, das PHP als falsch auswertet (dh 0, "", null). Ich habe den Code geändert, um ===
Eric Kigathi
4
Dies ist meistens sehr beeindruckend, aber um das Problem zu klären, weisen andere darauf hin, dass es mit einem Array wie diesem immer fehlschlagen wird [true,true,false,true]. Aber ich persönlich werde dies immer dann verwenden, wenn ich es mit einem Array zu tun habe, das keinen Booleschen Wert enthält false.
Billynoah
4
next()sollte NIEMALS in einer foreach-Schleife verwendet werden. Es bringt den internen Array-Zeiger durcheinander. Weitere Informationen finden Sie in der Dokumentation.
Drazzah
89

Eine vereinfachte Version des oben genannten und vorausgesetzt, Sie verwenden keine benutzerdefinierten Indizes ...

$len = count($array);
foreach ($array as $index => $item) {
    if ($index == 0) {
        // first
    } else if ($index == $len - 1) {
        // last
    }
}

Version 2 - Weil ich gekommen bin, um das else zu verabscheuen, es sei denn, es ist notwendig.

$len = count($array);
foreach ($array as $index => $item) {
    if ($index == 0) {
        // first
        // do something
        continue;
    }

    if ($index == $len - 1) {
        // last
        // do something
        continue;
    }
}
Hayden
quelle
8
Dies funktioniert auch für Objekte. Die anderen Lösungen funktionieren nur für Arrays.
Lamy
1
Dies ist die beste Antwort für mich, sollte aber komprimiert werden, kein Punkt, der die Länge außerhalb der foreach-Schleife deklariert: if ($ index == count ($ array) -1) {...}
Andrew
2
@ Andrew auf diese Weise zählen Sie die Elemente des Arrays für jede Iteration.
Pcarvalho
1
@peteroak Ja, tatsächlich würde dies die Leistung technisch beeinträchtigen, und je nachdem, was Sie zählen oder wie viele Schleifen von Bedeutung sein könnten. Also ignoriere meinen Kommentar: D
Andrew
4
@peteroak @Andrew Die Gesamtzahl der Elemente in einem Array wird intern als Eigenschaft gespeichert, sodass keine Leistungseinbußen auftreten if ($index == count($array) - 1). Siehe hier .
GreeKatrina
36

Sie können das erste und das letzte Element aus dem Array entfernen und separat verarbeiten.

So was:

<?php
$array = something();
$first = array_shift($array);
$last = array_pop($array);

// do something with $first
foreach ($array as $item) {
 // do something with $item
}
// do something with $last
?>

Das Entfernen aller Formatierungen in CSS anstelle von Inline-Tags würde Ihren Code verbessern und die Ladezeit verkürzen.

Sie können auch vermeiden, HTML mit PHP-Logik zu mischen, wann immer dies möglich ist.

Ihre Seite könnte viel lesbarer und wartbarer werden, indem Sie folgende Dinge trennen:

<?php
function create_menu($params) {
  //retrieve menu items 
  //get collection 
  $collection = get('xxcollection') ;
  foreach($collection as $c) show_collection($c);
}

function show_subcat($val) {
  ?>
    <div class="sub_node" style="display:none">
      <img src="../images/dtree/join.gif" align="absmiddle" style="padding-left:2px;" />
      <a id="'.$val['xsubcatid'].'" href="javascript:void(0)" onclick="getProduct(this , event)" class="sub_node_links"  >
        <?php echo $val['xsubcatname']; ?>
      </a>
    </div>
  <?php
}

function show_cat($item) {
  ?>
    <div class="node" >
      <img src="../images/dtree/plus.gif" align="absmiddle" class="node_item" id="plus" />
      <img src="../images/dtree/folder.gif" align="absmiddle" id="folder">
      <?php echo $item['xcatname']; ?>
      <?php 
        $subcat = get_where('xxsubcategory' , array('xcatid'=>$item['xcatid'])) ;
        foreach($subcat as $val) show_subcat($val);
      ?>
    </div>
  <?php
}

function show_collection($c) {
  ?>
    <div class="parent" style="direction:rtl">
      <img src="../images/dtree/minus.gif" align="absmiddle" class="parent_item" id="minus" />
      <img src="../images/dtree/base.gif" align="absmiddle" id="base">
      <?php echo $c['xcollectionname']; ?>
      <?php
        //get categories 
        $cat = get_where('xxcategory' , array('xcollectionid'=>$c['xcollectionid']));
        foreach($cat as $item) show_cat($item);
      ?>
    </div>
  <?php
}
?>
Carlos Lima
quelle
20

Ein Versuch, den ersten zu finden, wäre:

$first = true; 
foreach ( $obj as $value )
{
  if ( $first )
  {
    // do something
    $first = false; //in order not to get into the if statement for the next loops
  }
  else
  {
    // do something else for all loops except the first
  }
}
sstauross
quelle
3
Bitte bearbeiten Sie Ihre Antwort, um eine Erklärung hinzuzufügen, wie Ihr Code funktioniert und wie er das OP-Problem löst. Viele SO-Poster sind Neulinge und verstehen den von Ihnen geposteten Code nicht.
Ich alarmierte Alien
4
Diese Antwort sagt nichts darüber aus, wie Sie feststellen können, ob Sie sich in der letzten Iteration der Schleife befinden. Dies ist jedoch ein gültiger Versuch einer Antwort und sollte nicht als keine Antwort gekennzeichnet werden. Wenn es dir nicht gefällt, solltest du es ablehnen und nicht markieren.
ArtOfWarfare
Es ist klar, dass er in der ersten Iteration die erste Bedingung eingibt und diesen Wert dann in false ändert. Auf diese Weise wird er nur einmal in die erste Iteration aufgenommen.
Mohamed23gharbi
20

Einfach das funktioniert!

// Set the array pointer to the last key
end($array);
// Store the last key
$lastkey = key($array);  
foreach($array as $key => $element) {
    ....do array stuff
    if ($lastkey === key($array))
        echo 'THE LAST ELEMENT! '.$array[$lastkey];
}

Vielen Dank für Ihre @billynoah das Aussortieren Ende Problem.

Sydwell
quelle
3
Beste! Ich würde nur klarstellen if ($key === $lastkey).
Krzysztof Przygoda
2
sollte nicht das if ($lastkey === $key)?
Kinetic
1
Ich bekomme:PHP Warning: key() expects parameter 1 to be array, integer given in php shell code on line 1
Billynoah
1
@ Sydney - lesen Sie den Fehler. key()bekommt eine ganze Zahl, nicht end(). end()" gibt den Wert des letzten Elements zurück " und key()erwartet ein Array als Eingabe.
Billynoah
11

1: Warum nicht eine einfache forAussage verwenden? Angenommen, Sie verwenden ein reales Array und kein Array, können IteratorSie leicht überprüfen, ob die Zählervariable 0 oder eins weniger als die Gesamtzahl der Elemente ist. Meiner Meinung nach ist dies die sauberste und verständlichste Lösung ...

$array = array( ... );

$count = count( $array );

for ( $i = 0; $i < $count; $i++ )
{

    $current = $array[ $i ];

    if ( $i == 0 )
    {

        // process first element

    }

    if ( $i == $count - 1 )
    {

        // process last element

    }

}

2: Sie sollten in Betracht ziehen, verschachtelte Mengen zum Speichern Ihrer Baumstruktur zu verwenden. Zusätzlich können Sie das Ganze durch rekursive Funktionen verbessern.

Okoman
quelle
Wenn Sie ein verwenden formöchten, können Sie eine Schleife von 1bis ausführen n-1und das ifs aus dem Körper entfernen. Es macht keinen Sinn, sie wiederholt zu überprüfen.
Mpen
9

Beste Antwort:

$arr = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

foreach ($arr as $a) {

// This is the line that does the checking
if (!each($arr)) echo "End!\n";

echo $a."\n";

}
Ivan
quelle
2
Dies schlägt fehl, wenn Sie nur ein Element im Array haben.
Memochipan
4
Es ist schnell und einfach, solange Sie sicher sein können, dass sich immer mehr als ein Element im Array befindet (wie Memochipan sagte). Für mich ist es also keine ausfallsichere Lösung - keine „beste Antwort“.
Seika85
5
jedes () wird ab PHP 7.2.0 VERRINGERT . Bitte sehen Sie auch php.net/manual/en/function.each.php
Flow
8

Die effizienteste Antwort von @morg funktioniert im Gegensatz dazu foreachnur für richtige Arrays, nicht für Hash-Map-Objekte. Diese Antwort vermeidet den Overhead einer bedingten Anweisung für jede Iteration der Schleife, wie in den meisten dieser Antworten (einschließlich der akzeptierten Antwort), indem das erste und das letzte Element spezifisch behandelt und die mittleren Elemente durchlaufen werden.

Das array_keys Funktion kann verwendet werden, damit die effiziente Antwort wie folgt funktioniert foreach:

$keys = array_keys($arr);
$numItems = count($keys);
$i=0;

$firstItem=$arr[$keys[0]];

# Special handling of the first item goes here

$i++;
while($i<$numItems-1){
    $item=$arr[$keys[$i]];
    # Handling of regular items
    $i++;
}

$lastItem=$arr[$keys[$i]];

# Special handling of the last item goes here

$i++;

Ich habe diesbezüglich kein Benchmarking durchgeführt, aber der Schleife wurde keine Logik hinzugefügt. Dies ist der größte Leistungseinbruch. Daher würde ich vermuten, dass die mit der effizienten Antwort gelieferten Benchmarks ziemlich nahe beieinander liegen.

Wenn Sie so etwas funktionalisieren wollten , habe ich hier eine solche iterateList-Funktion ausprobiert . Möglicherweise möchten Sie jedoch den Kerncode vergleichen, wenn Sie sich große Sorgen um die Effizienz machen. Ich bin mir nicht sicher, wie viel Overhead der gesamte Funktionsaufruf mit sich bringt.

TheMadDeveloper
quelle
6

Bei Skripten zur Generierung von SQL-Abfragen oder bei Skripten, die eine andere Aktion für das erste oder letzte Element ausführen, ist es viel schneller (fast doppelt so schnell), unnötige Variablenprüfungen zu vermeiden.

Die aktuell akzeptierte Lösung verwendet eine Schleife und eine Prüfung innerhalb der Schleife, die bei jeder einzelnen Wiederholung durchgeführt wird. Der richtige (schnelle) Weg, dies zu tun, ist der folgende:

$numItems = count($arr);
$i=0;
$firstitem=$arr[0];
$i++;
while($i<$numItems-1){
    $some_item=$arr[$i];
    $i++;
}
$last_item=$arr[$i];
$i++;

Ein kleiner hausgemachter Benchmark zeigte Folgendes:

test1: 100000 Läufe von Model Morg

Zeit: 1869.3430423737 Millisekunden

Test2: 100000 Modellläufe, wenn zuletzt

Zeit: 3235.6359958649 Millisekunden

Und so ist es ziemlich klar, dass der Scheck viel kostet, und natürlich wird es noch schlimmer, je mehr variable Schecks Sie hinzufügen;)

Morg.
quelle
Ihr Code funktioniert nur, wenn Sie sicher sein können, dass Sie inkrementelle Ganzzahlschlüssel haben müssen. $arr = array('one' => "1 1 1", 4 => 'Four', 1 => 'One'); $numItems = count($arr); $i=0; $firstitem=$arr[0]; echo $i . ': ' . $firstitem . ", "; $i++; while($i<$numItems-1){ $some_item=$arr[$i]; echo $i . ': ' . $some_item . ", "; $i++; } $last_item=$arr[$i]; echo $i . ': ' . $last_item . ", "; $i++;wird ausgegeben:0: , 1: One, 2: ,
Seika85
Das Casting einer Hash-Map in ein Array ist ein undefinierbares Verhalten. Das "Objekt" array()wird erstellt, {'one':"1 1 1",0:"",1:"One",2:"",3:"",4:"Four"}aber die leeren Elemente werden mit count ignoriert. Sie zählen die Anzahl der definierten "Dinge". BOUNTY SACRIFICE EINGEHEND! Diese Antwort verdient Kopfgeld, aber wenn @Morg. weg, es ist sinnlos. Ich würde einer Person Kopfgeld geben, die SO wahrscheinlich nicht mehr verwenden wird! Wenn er zurückkommt und seine Antwort verbessert, verdient er das Kopfgeld!
mjz19910
Wie @ mjz19910 feststellt, sind Hash-Maps und Arrays nicht austauschbar. Sie können jedoch die Eigenschaften des Hashs mit der array_keysFunktion abrufen , die Sie wie ein Array behandeln können . Siehe meine "verbesserte" Antwort .
TheMadDeveloper
Das ist, was ich für die Abfrage benutze:$querySet = ""; foreach ($fieldSet as $key=>$value) { $value = $db->dbLink->quote($value); $querySet .= "$key = $value, "; } $querySet = substr_replace($querySet, "", -2); $queryString = "UPDATE users SET $querySet WHERE user_ID = '$user_ID'";
Rovshan Mamedov
5

Mit Schlüsseln und Werten funktioniert dies auch:

foreach ($array as $key => $value) {
    if ($value === end($array)) {
        echo "LAST ELEMENT!";
    }
}
Benibr
quelle
2
Auf diese Weise vergleichen Sie Werte und funktionieren nicht, wenn ein Array zwei gleiche Elemente enthält.
Krzysztof Przygoda
5

Die Verwendung einer booleschen Variablen ist immer noch die zuverlässigste, selbst wenn Sie das erste Auftreten von a überprüfen möchten $value (ich fand es in meiner Situation und in vielen Situationen nützlicher) , wie folgt:

$is_first = true;

foreach( $array as $value ) {
    switch ( $value ) {
        case 'match':
            echo 'appeared';

            if ( $is_first ) {
                echo 'first appearance';
                $is_first = false;
            }

            break;
        }
    }

    if( !next( $array ) ) {
        echo 'last value';
    }
}

Wie wäre !next( $array )es dann, den letzten zu finden, der $valuezurückkehrt, truewenn es keinen next()Wert zum Iterieren gibt.

Und ich bevorzuge es, eine forSchleife zu verwenden , anstatt foreacheinen Zähler wie diesen zu verwenden:

$len = count( $array );
for ( $i = 0; $i < $len; $i++ ) {
    $value = $array[$i];
    if ($i === 0) {
        // first
    } elseif ( $i === $len - 1 ) {
        // last
    }
    // …
    $i++;
}
5ervant
quelle
4

Ich bin auf diesen Thread gestoßen, als ich das gleiche Problem habe. Ich brauche nur das erste Element, dann analysiere ich meinen Code erneut, bis mir dies einfiel.

$firstElement = true;

foreach ($reportData->result() as $row) 
{
       if($firstElement) { echo "first element"; $firstElement=false; }
       // Other lines of codes here
}

Die obigen Codes sind großartig und vollständig, aber wenn Sie nur das erste Element benötigen, können Sie diesen Code ausprobieren.

paul
quelle
2

Ich bin mir nicht sicher, ob es noch notwendig ist. Die folgende Lösung sollte jedoch mit Iteratoren funktionieren und ist nicht erforderlich count.

<?php

foreach_first_last(array(), function ($key, $value, $step, $first, $last) {
    echo intval($first), ' ', intval($last), ' ', $step, ' ', $value, PHP_EOL;
});

foreach_first_last(array('aa'), function ($key, $value, $step, $first, $last) {
    echo intval($first), ' ', intval($last), ' ', $step, ' ', $value, PHP_EOL;
});
echo PHP_EOL;

foreach_first_last(array('aa', 'bb', 'cc'), function ($key, $value, $step, $first, $last) {
    echo intval($first), ' ', intval($last), ' ', $step, ' ', $value, PHP_EOL;
});
echo PHP_EOL;

function foreach_first_last($array, $cb)
{
    $next = false;
    $current = false;
    reset($array);
    for ($step = 0; true; ++$step) {
        $current = $next;
        $next = each($array);
        $last = ($next === false || $next === null);
        if ($step > 0) {
            $first = $step == 1;
            list ($key, $value) = $current;
            if (call_user_func($cb, $key, $value, $step, $first, $last) === false) {
                break;
            }
        }
        if ($last) {
            break;
        }
    }
}
vbarbarosh
quelle
0

Sie können auch eine anonyme Funktion verwenden:

$indexOfLastElement = count($array) - 1;
array_walk($array, function($element, $index) use ($indexOfLastElement) {
    // do something
    if (0 === $index) {
        // first element‘s treatment
    }
    if ($indexOfLastElement === $index) {
        // last not least
    }
});

Drei weitere Dinge sollten erwähnt werden:

  • Wenn Ihr Array nicht streng (numerisch) indiziert ist, müssen Sie Ihr Array durchleiten array_values zuerst .
  • Wenn Sie das ändern müssen, müssen $elementSie es als Referenz übergeben (&$element ).
  • Alle Variablen von außerhalb der anonymen Funktion, die Sie innerhalb benötigen, müssen neben $indexOfLastElementdem useKonstrukt aufgelistet werden, bei Bedarf erneut als Referenz.
undko
quelle
0

Sie können den Zähler und die Array-Länge verwenden.

    $ array = array (1,2,3,4);

    $ i = 0;
    $ len = count ($ array);
    foreach ($ array als $ item) {
        if ($ i === 0) {
            // zuerst
        } else if ($ i === $ len - 1) {
            // letzte
        }}
        //…
        $ i ++;
    }}
Jesus Erwin Suarez
quelle
0
foreach ($arquivos as $key => $item) {
   reset($arquivos);
   // FIRST AHEAD
   if ($key === key($arquivos) || $key !== end(array_keys($arquivos)))
       $pdf->cat(null, null, $key);

   // LAST
   if ($key === end(array_keys($arquivos))) {
       $pdf->cat(null, null, $key)
           ->execute();
   }
}
Joao Paulo Pinheiro
quelle
0

Verwenden von reset ($ array) und end ($ array)

<?php

    $arrays = [1,2,3,4,5];

    $first  = reset($arrays);
    $last   = end($arrays);    

    foreach( $arrays as $array )
    {

        if ( $first == $array )
        {
            echo "<li>{$array} first</li>";
        }
        else if ( $last == $array )
        {
            echo "<li>{$array} last</li>";
        }
        else
        {
            echo "<li>{$array}</li>";
        }                

    }

Demo repl.it

Anteliebe
quelle
-2

Versuche dies:

function children( &$parents, $parent, $selected ){
  if ($parents[$parent]){
    $list = '<ul>';
    $counter = count($parents[$parent]);
    $class = array('first');
    foreach ($parents[$parent] as $child){
      if ($child['id'] == $selected)  $class[] = 'active';
      if (!--$counter) $class[] = 'last';
      $list .= '<li class="' . implode(' ', $class) . '"><div><a href="]?id=' . $child['id'] . '" alt="' . $child['name'] . '">' . $child['name'] . '</a></div></li>';
      $class = array();
      $list .= children($parents, $child['id'], $selected);
    }
    $list .= '</ul>';
    return $list;
  }
}
$output .= children( $parents, 0, $p_industry_id);
PureField
quelle