Як визначити першу та останню ітерацію в циклі foreach?


494

Питання просте. У мене в foreachциклі є цикл:

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

У цьому циклі я хочу по-різному реагувати, коли ми перебуваємо на першій чи останній ітерації.

Як це зробити?

Відповіді:


426

Ви можете використовувати лічильник:

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

24
Я не думаю, що тут має відбуватися знімання, оскільки це також працює правильно і все ще не є таким сміттям, як використання array_shiftта array_pop. Хоча це рішення, яке я придумав, якби мені довелося реалізувати таке, я зараз дотримуюся відповіді Рока Краля .
shadyyx

15
Якщо мені потрібен лічильник, я вважаю за краще використовувати цикл FOR замість FOREACH.
rkawano

10
Якщо ви користуєтеся $i = 1, вам не доведеться хвилюватися $len - 1, просто використовуйте $len.
аксу

2
@Twan Як правильно відповідає точка 3? Або взагалі стосується цього питання, оскільки воно включає HTML? Це питання PHP, зрозуміло ... і якщо мова йде про семантику розмітки, то це зводиться до набагато глибших фактів, ніж "власне благбла завжди краще, ніж блабла (це навіть не моя думка, це чистий факт)".
Deji

1
@rkawano, але ви не можете отримати названий ключ, якщо ви використовуєте цикл FOR
Fahmi

1014

Якщо ви віддаєте перевагу рішенню, яке не потребує ініціалізації лічильника поза циклом, пропоную порівняти поточний ключ ітерації з функцією, яка повідомляє вам останній / перший ключ масиву.

Це стає дещо ефективнішим (і читабельніше) з майбутнім PHP 7.3.

Рішення для PHP 7.3 і вище:

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

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

Рішення для всіх версій PHP:

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

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

44
Фантастична відповідь! Набагато чистіше, ніж використовувати купу масивів.
Пол J

14
Це повинно бути бульбашкою аж до вершини, тому що це правильна відповідь. Ще одна перевага цих функцій перед використанням array_shift та array_pop полягає в тому, що масиви залишаються неушкодженими, на випадок, якщо вони знадобляться в подальшому. +1 для обміну знаннями просто заради цього.
Awemo

13
це, безумовно, найкращий спосіб, якщо ви хочете зберегти чистий код. Я вже збирався його схвалити, але зараз я не впевнений, що функціональні витрати цих методів масиву того варті. Якщо ми просто говоримо про останній елемент, то це end()+ key()над кожною ітерацією циклу - якщо це обидва, то кожен раз викликають 4 методи. Зрозуміло, це були б дуже легкі операції і, ймовірно, є лише пошуковими вказівниками, але потім документи продовжують вказувати це reset()та end() змінювати внутрішній покажчик масиву - так це швидше, ніж лічильник? можливо, ні.
pospi

19
Я не думаю, що вам слід видавати скидання ($ масив) всередині foreach. З офіційної документації (www.php.net/foreach): "Оскільки Foreach покладається на внутрішній покажчик масиву, зміна його в циклі може призвести до несподіваної поведінки". І скидання робить саме це (www.php.net/reset): "Встановіть внутрішній вказівник масиву на його перший елемент"
Gonçalo Queirós

11
@ GonçaloQueirós: Це працює. Foreach працює над копією масиву. Однак якщо ви все ще стурбовані, не соромтеся перенести reset()виклик перед програмою foreach та кешувати результат $first.
Rok Kralj

121

Щоб знайти останній елемент, я вважаю, що цей фрагмент коду працює щоразу:

foreach( $items as $item ) {
    if( !next( $items ) ) {
        echo 'Last Item';
    }
}

2
У цьому занадто мало результатів, чи є недолік використання цього?
Кевін Куйл


2
@Kevin Kuyl - Як згадував Панг вище, якщо масив містить елемент, який PHP оцінює як хибний (тобто 0, "", null), цей метод матиме несподівані результати. Я змінив код, щоб використовувати ===
Ерік Кігаті

4
Це, як правило, дуже дивовижно, але якщо з'ясувати, на що проблеми вказують інші, воно незмінно вийде з масиву на зразок [true,true,false,true]. Але особисто я буду використовувати це в будь-який час, коли я маю справу з масивом, який не містить булевих false.
billynoah

4
next()не повинні НІКОЛИ бути використані всередині циклу Еогеасп. Він псує внутрішній покажчик масиву. Перегляньте документацію для отримання додаткової інформації.
Дража

89

Більш спрощена версія вищезазначеного та припускаючи, що ви не використовуєте власні індекси ...

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

Версія 2 - Тому що я не люблю використовувати інше, якщо не потрібно.

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

    if ($index == $len - 1) {
        // last
        // do something
        continue;
    }
}

8
Це працює і для об’єктів. Інші рішення працюють лише для масивів.
Ламі

1
Це найкраща відповідь для мене, але її слід стиснути, жодна точка не оголошує довжину поза циклом foreach: if ($ index == count ($ масив) -1) {...}
Андрій

2
@Аndrew таким чином ви продовжуєте рахувати елементи масиву для кожної ітерації.
pcarvalho

1
@peteroak Так, це насправді технічно шкодить продуктивності та залежно від того, який підрахунок чи кількість циклів може бути значним. Тож ігноруйте мій коментар: D
Андрій

4
@peteroak @Andrew Загальна кількість елементів у масиві зберігається як властивість внутрішньо, так що не буде жодних звернень до продуктивності if ($index == count($array) - 1). Дивіться тут .
GreeKatrina

36

Ви можете видалити перший і останній елементи з масиву та обробити їх окремо.

Подобається це:

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

Видалення всього форматування до CSS замість вбудованих тегів покращить ваш код та прискорить час завантаження.

Ви також можете уникати змішування HTML з логікою php, коли це можливо.

Ваша сторінка може бути набагато зручнішою для читання та ремонтування, розділивши такі речі:

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

20

Спробою знайти першу було б:

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

3
Будь ласка, відредагуйте свою відповідь, щоб додати пояснення того, як працює ваш код та як він вирішує проблему ОП. Багато плакатів ТА є новачками і не зрозуміють код, який ви опублікували.
я насторожив прибульця

4
Ця відповідь не говорить про те, як визначити, чи знаходитесь ви в останній ітерації циклу. Однак це правдива спроба відповіді, і її не слід позначати як невідповідь. Якщо вам це не подобається, вам слід голосувати за нього, а не прапор.
ArtOfWarfare

Зрозуміло, що під час першої ітерації він введе першу умову, а потім змінить її значення на помилкове, і таким чином він потрапить у першу ітерацію лише один раз.
Mohamed23gharbi

20

Просто це працює!

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

Дякую @billynoah за те, що ви розібрали кінцевий випуск.


3
Найкраще! Я б тільки уточнив if ($key === $lastkey).
Krzysztof Przygoda

2
не повинно бути цього if ($lastkey === $key)?
Кінетик

1
Я отримую:PHP Warning: key() expects parameter 1 to be array, integer given in php shell code on line 1
billynoah

1
@Sydwell - прочитати помилку. key()отримує ціле число, а не end(). end()" повертає значення останнього елемента " і key()очікує масив як вхідний.
billynoah


11

1: Чому б не використати просте forтвердження? Якщо припустити, що ви використовуєте реальний масив, а не Iteratorви, ви можете легко перевірити, чи змінна лічильника на 0 або на одну менша, ніж на всю кількість елементів. На мою думку, це найбільш чисто і зрозуміле рішення ...

$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: Вам слід розглянути можливість використання вкладених наборів для зберігання вашої структури дерева. Крім того, ви можете покращити все, використовуючи рекурсивні функції.


Якщо ви збираєтесь використовувати a, forви можете перевести цикл з 1на n-1та вийняти ifs з тіла. Немає сенсу перевіряти їх повторно.
mpen

9

Найкраща відповідь:

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

}

2
Це не вдається, якщо у масиві є лише один елемент.
Мемочіпан

4
Це швидко і легко, якщо ви можете бути впевнені, що в масиві завжди є більше одного лементу (як сказав Мемочіпан). Тож для мене це небезпечне рішення - немає «найкращої відповіді».
Seika85

5
кожний () буде Знятий з PHP 7.2.0 . Також дивіться php.net/manual/en/function.each.php
Потік

8

Найбільш ефективна відповідь від @morg, на відміну від цього foreach, працює лише для правильних масивів, а не об’єктів хеш-карти. Ця відповідь дозволяє уникнути накладних витрат на умовне твердження для кожної ітерації циклу, як у більшості цих відповідей (включаючи прийняту відповідь) конкретно обробляючи перший і останній елемент, і перебираючи петлі на середні елементи.

array_keysФункція може бути використана для ефективної роботи відповіді , як 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++;

Я не робив тестування з цього приводу, але до циклу не додано жодної логіки, яка була найбільшим ударом для продуктивності, тому я б підозрював, що орієнтири, що надаються з ефективною відповіддю, досить близькі.

Якщо ви хотіли функціонувати подібну річ, я тут перейнявся такою функцією iterateList . Хоча, можливо, ви захочете порівняти код суті, якщо вас дуже турбує ефективність. Я не впевнений, скільки накладних витрат викликає все виклик функції.


6

Для створення SQL-запитів, що генерують скрипти, або будь-якого іншого, що робить різні дії для перших або останніх елементів, набагато швидше (майже вдвічі швидше) уникнути використання не потрібних перевірок змінних.

Поточне прийняте рішення використовує цикл і перевірку в циклі, які будуть зроблені every_single_iteration, правильний (швидкий) спосіб зробити це наступний:

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

Невеликий домашній тест показав наступне:

test1: 100000 пробіжок модельного моргу

час: 1869,3430423737 мілісекунд

test2: 100000 запусків моделі, якщо остання

час: 3235.6359958649 мілісекунд

І тому цілком зрозуміло, що чек коштує чимало, і, звичайно, стає ще гірше, чим більше змінних чеків, які ви додаєте;)


Ваш код працює лише в тому випадку, якщо ви можете бути впевнені, що повинні бути збільшені цілі ключі. $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++;вийде:0: , 1: One, 2: ,
Seika85

закидання хеш-карти до масиву - невизначна поведінка, array()зроблений "Об'єкт" , {'one':"1 1 1",0:"",1:"One",2:"",3:"",4:"Four"}але порожні елементи ігноруються підрахунком, ви рахуєте кількість визначених "речей" !! СВЯТИ СВЯТИ БУНТИ! Ця відповідь заслуговує на винагороду, але якщо @Morg. пішли, це безглуздо. Я б подарував щедрість людині, яка, ймовірно, не буде використовувати SO знову! Якщо він повернеться і вдосконалює свою відповідь, він заслуговує на винагороду!
mjz19910

Як зазначає @ mjz19910, хеш-карти та масиви не є взаємозамінними. Однак ви можете отримати властивості хеша за допомогою array_keysфункції, яку ви можете розглядати як масив. Дивіться мою "покращену" відповідь .
TheMadDeveloper

Ось що я використовую для запиту:$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'";
Ровшан Мамедов

5

З ключами та значеннями це також працює:

foreach ($array as $key => $value) {
    if ($value === end($array)) {
        echo "LAST ELEMENT!";
    }
}

2
Таким чином ви порівнюєте значення і не працює, якщо масив містить два однакових елемента.
Krzysztof Przygoda

5

Використання булевої змінної все ще є найбільш надійним, навіть якщо ви хочете перевірити першу появу $value (я вважав, що це корисніше в моїй ситуації та в багатьох ситуаціях) , таких як ця:

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

Тоді як про те, !next( $array )щоб знайти останню, $valueяка повернеться, trueякщо її немаєnext() значення для ітерації.

І я вважаю за краще використовувати forцикл замість того, foreachякби я збирався використовувати лічильник, як це:

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

4

Я натрапив на цю нитку, коли у мене однакова проблема. Мені потрібно лише отримати перший елемент, після чого я повторно проаналізую свій код, поки це не прийшло мені в голову.

$firstElement = true;

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

Наведені вище коди є чудовими та повними, але якщо вам потрібен лише перший елемент, тоді ви можете спробувати цей код.


2

Не впевнений, чи все-таки це необхідно. Але наступне рішення має працювати з ітераторами і не вимагає 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;
        }
    }
}

0

Ви також можете використовувати анонімну функцію:

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

Слід зазначити ще три речі:

  • Якщо ваш масив не індексується строго (числово), ви повинні передати свій масив array_valuesспочатку.
  • Якщо вам потрібно змінити, $elementви повинні передати його посиланням ( &$element).
  • Будь-які змінні за межами анонімної функції, яка вам потрібна всередині, вам доведеться перелічити їх поруч із $indexOfLastElementвнутрішньою useконструкцією, знову ж таки за посиланням, якщо потрібно.

0

Ви можете використовувати довжину лічильника та масиву.

    $ масив = масив (1,2,3,4);

    $ i = 0;
    $ len = count ($ масив);
    foreach ($ масив як $ item) {
        якщо ($ i === 0) {
            // спочатку
        } else if ($ i === $ len - 1) {
            // останній
        }
        //…
        $ i ++;
    }

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

0

Використання скидання ($ масив) та закінчення ($ масив)

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

    }

Демонстраційний репл.it


-2

Спробуйте це:

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);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.