Jak określić pierwszą i ostatnią iterację w pętli foreach?

Pytanie jest proste. Mam pętlę foreach w kodzie:

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

W tej pętli chcę reagować inaczej, gdy jesteśmy w pierwszej lub ostatniej iteracji.

Jak to zrobić?
Author: Rok Kralj, 2009-07-01

18 answers

Przydałby się licznik:

$i = 0;
$len = count($array);
foreach ($array as $item) {
    if ($i == 0) {
        // first
    } else if ($i == $len - 1) {
        // last
    }
    // …
    $i++;
}
 349
Author: Gumbo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-10 02:25:07

Jeśli wolisz rozwiązanie, które nie wymaga inicjalizacji licznika poza pętlą, proponuję porównanie bieżącego klucza iteracji z funkcją, która mówi ostatni / pierwszy klucz tablicy.

Staje się to nieco bardziej wydajne (i czytelne) w nadchodzącym PHP 7.3.

Rozwiązanie dla PHP 7.3 i nowszych:

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

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

Rozwiązanie dla wszystkich wersji PHP:

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

    end($array);
    if ($key === key($array))
        echo 'LAST ELEMENT!';
}
 850
Author: Rok Kralj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-18 20:46:27

Aby znaleźć ostatni element, ten fragment kodu działa za każdym razem:

foreach( $items as $item ) {
    if( !next( $items ) ) {
        echo 'Last Item';
    }
}
 79
Author: Yojance,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-23 20:53:10

Bardziej uproszczona wersja powyższego i zakładając, że nie używasz indeksów niestandardowych...

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

Wersja 2-ponieważ doszedłem do brzydzenia używaniem else, chyba że jest to konieczne.

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

    if ($index == $len - 1) {
        // last
        // do something
        continue;
    }
}
 68
Author: Hayden,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-03 00:24:54

Można usunąć pierwszy i ostatni element z tablicy i przetworzyć je oddzielnie.
Tak:

<?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
?>

Usunięcie całego formatowania do CSS zamiast znaczników inline poprawi kod i przyspieszy czas ładowania.

Można również uniknąć mieszania HTML z logiką php, gdy tylko jest to możliwe.
Twoja strona może być o wiele bardziej czytelna i łatwa do utrzymania, rozdzielając takie rzeczy:

<?php
function create_menu($params) {
  //retirive 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
}
?>
 31
Author: Carlos Lima,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-01 17:46:17

Próba znalezienia pierwszego to:

$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
  }
}
 14
Author: sstauross,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-29 11:30:46

Po prostu to działa!

//Store the last key
$lastkey = key(end($array)); 
foreach($array as $key => $element) {
    ....do array stuff
    if ($lastkey === key($array))
        echo 'LAST ELEMENT!';
}
 14
Author: Sydwell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-25 20:18:29

1: Dlaczego nie użyć prostej for deklaracji? Zakładając, że używasz prawdziwej tablicy, a nie Iterator, możesz łatwo sprawdzić, czy zmienna counter jest o 0, czy o 1 mniej niż cała liczba elementów. Moim zdaniem jest to najbardziej czyste i zrozumiałe rozwiązanie...

$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: powinieneś rozważyć użycie zagnieżdżonych zestawów do przechowywania struktury drzewa. Dodatkowo możesz poprawić całość używając funkcji rekurencyjnych.

 10
Author: okoman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-30 23:18:22

Najlepsza odpowiedź:

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

}
 8
Author: Ivan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-11 05:29:53

W przypadku skryptów generujących zapytania SQL lub czegokolwiek, co wykonuje inną akcję dla pierwszego lub ostatniego elementu, znacznie szybciej (prawie dwa razy szybciej) jest unikanie sprawdzania unneccessary.

Aktualnie przyjęte rozwiązanie używa pętli i sprawdzenia wewnątrz pętli, które będzie wykonane every_single_iteration, poprawnym (szybkim) sposobem na to jest:

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

Mały, domowy benchmark pokazał, co następuje:

Test1: 100000 przebiegów modelu morg

Czas: 1869.3430423737 milisekund

Test2: 100000 uruchomień modelu, jeśli ostatni

Czas: 3235.6359958649 milisekund

I jest więc całkiem jasne, że kontrola kosztuje dużo, a oczywiście jest jeszcze gorzej, im więcej zmiennych kontroli dodasz ;)

 5
Author: Morg.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-09 13:33:32

Najbardziej efektywna odpowiedź od @morg, w przeciwieństwie do foreach, działa tylko dla odpowiednich tablic, a nie obiektów hash map. Ta odpowiedź pozwala uniknąć napowietrzności instrukcji warunkowej dla każdej iteracji pętli, tak jak w większości tych odpowiedzi (w tym zaakceptowanej odpowiedzi) przez , a konkretnie obsługę pierwszego i ostatniego elementu oraz zapętlanie elementów środkowych.

The array_keys funkcja może być użyta, aby efektywna odpowiedź działała jak 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++;

I nie zrobiłem benchmarking na ten temat, ale nie logika została dodana do pętli, która jest największym hitem wydajności dzieje, więc podejrzewam, że benchmarki dostarczane z efektywną odpowiedź są dość blisko.

Jeśli chcesz funkcjonalizować tego typu rzeczy, podjąłem zamach na takąfunkcję iterateList tutaj . Chociaż, możesz chcieć porównać kod gist, jeśli jesteś bardzo zaniepokojony wydajnością. Nie jestem pewien, ile kosztują wszystkie wywołanie funkcji wprowadza.

 5
Author: TheMadDeveloper,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:46

Używanie zmiennej logicznej jest nadal najbardziej niezawodne, nawet jeśli chcesz sprawdzić pierwsze pojawienie się $value (okazało się, że jest bardziej przydatny w mojej sytuacji i w wielu sytuacjach) , taki jak ten:

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

To może !next( $array ) znaleźć ostatnią $value, która zwróci true, jeśli nie ma next() wartości do iteracji.

A ja wolę używać pętli for zamiast foreach, gdybym miał użyć licznika, tak:

$len = count( $array );
for ( $i = 0; $i < $len; $i++ ) {
    $value = $array[$i];
    if ($i === 0) {
        // first
    } elseif ( $i === $len - 1 ) {
        // last
    }
    // …
    $i++;
}
 4
Author: 5ervant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-27 17:26:34

Z kluczami i wartościami działa to również:

foreach ($array as $key => $value) {
    if ($value === end($array)) {
        echo "LAST ELEMENT!";
    }
}
 3
Author: Benibr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-04 00:51:11

Natknąłem się na ten wątek, gdy mam ten sam problem. Muszę tylko zdobyć pierwszy element, a potem ponownie przeanalizować mój kod, aż to przyszło mi do głowy.

$firstElement = true;

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

Powyższe kody są świetne i kompletne, ale jeśli potrzebujesz tylko pierwszego elementu, możesz wypróbować ten kod.

 3
Author: paul,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-11 11:34:21

Nie wiem, czy to jeszcze konieczne. Poniższe rozwiązanie powinno jednak działać z iteratorami i nie wymaga 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;
        }
    }
}
 1
Author: vbarbarosh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-06 15:39:32

Możesz też użyć funkcji anonimowej:

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

Należy wymienić jeszcze trzy rzeczy:

  • jeśli tablica nie jest indeksowana ściśle (numerycznie), musisz najpierw przepuścić tablicę przez array_values.
  • Jeśli chcesz zmodyfikować $element musisz przekazać go przez referencję (&$element).
  • wszelkie zmienne spoza funkcji anonymous, które potrzebujesz w środku, będziesz musiał wymienić je obok $indexOfLastElement wewnątrz use konstruktu, ponownie przez odniesienie w razie potrzeby.
 0
Author: undko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-26 00:41:05

Możesz użyć licznika i długości tablicy.

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

    $i = 0;
    $len = count($array);
    foreach ($array as $item) {
        if ($i === 0) {
            // first
        } else if ($i === $len - 1) {
            // last
        }
        // …
        $i++;
    }
 0
Author: Jesus Erwin Suarez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-02 00:41:32

Spróbuj tego:

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);
 -1
Author: PureField,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-26 04:44:29