Web

Как определить первую и последнюю итерацию в цикле foreach

Вопрос простой.  В моем коде есть цикл foreach:

foreach($array as $element) {

    // некий код

}

 В этом цикле я хочу по-разному реагировать, когда мы находимся на первой или последней итерации. Как это сделать?

 

Ответ 1

Если вы предпочитаете решение, которое не требует инициализации счетчика вне цикла, вы можете сравнить текущий ключ итерации с функцией, которая сообщает вам последний/первый ключ массива.

PHP 7.3 и новее:

foreach ($array as $key => $element) {

    if ($key === array_key_first($array)) {

        echo 'ПЕРВЫЙ ЭЛЕМЕНТ!';

    }

    if ($key === array_key_last($array)) {

        echo 'ПОСЛЕДНИЙ ЭЛЕМЕНТ!';

    }

}

PHP 7.2:

foreach ($array as $key => $element) {

    reset($array);

    if ($key === key($array)) {

        echo 'ПЕРВЫЙ ЭЛЕМЕНТ!';

    }

    end($array);

    if ($key === key($array)) {

        echo 'ПОСЛЕДНИЙ ЭЛЕМЕНТ!';

    }

}

 

Ответ 2

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

$len = count($array);

foreach ($array as $index => $item) {

    if ($index == 0) {

        // первая итерация

    } else if ($index == $len - 1) {

        // последняя итерация

    }

}

 Версия 2 — потому что я считаю нецелесообразным использовать «else» без необходимости.

$len = count($array);

foreach ($array as $index => $item) {

    if ($index == 0) {

        // первая итерация

        // некоторый код

        continue;

    }

    if ($index == $len - 1) {

        // последняя итерация

        // некоторый код

        continue;

    }

}

 

Ответ 3

Вы можете удалить первый и последний элементы из массива и обработать их отдельно.

<?php

$array = something();

$first = array_shift($array);

$last = array_pop($array);

// делаем что-нибудь с $first

foreach ($array as $item) {

// делаем что-нибудь с $item

}

// делаем что-нибудь с $last

?>

Удаление всего форматирования в CSS вместо встроенных тегов улучшит ваш код и ускорит время загрузки. Вы также можете избегать смешивания HTML с логикой php, когда это возможно. Страницу можно было бы сделать более читаемой и удобной в обслуживании, разделив код следующим образом:

<?php

function create_menu($params) {

  //получение пунктов меню 

  //получить коллекцию 

  $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

        //получение категорий 

        $cat = get_where('xxcategory' , array('xcollectionid'=>$c['xcollectionid']));

        foreach($cat as $item) show_cat($item);

      ?>

    </div>

  <?php

}

?>

 

Ответ 4

1: Почему бы не использовать простое утверждение for? Предполагая, что вы используете реальный массив, а не Iterator, вы можете легко проверить, равна ли переменная счетчика нулю или на единицу меньше, чем все количество элементов. На мой взгляд, это наиболее чистое и понятное решение...

$array = array( ... );

$count = count( $array );

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

    $current = $array[ $i ];

    if ( $i == 0 ) {

        // обработка первого элемента

    }

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

        // обработка последнего элемента

    }

}

2: Вам следует рассмотреть возможность использования вложенных наборов для хранения древовидной структуры. Кроме того, вы можете улучшить все это, используя рекурсивные функции.

 

 Ответ 5

foreach работает только для правильных массивов, а не для объектов хеш-карты. Этот ответ позволяет избежать накладных расходов на условный оператор для каждой итерации цикла, как и в большинстве этих ответов (включая принятый ответ), путем специальной обработки первого и последнего элементов и цикла по средним элементам. Функция array_keys() может быть использована для эффективной работы в данном ответе, так же как и foreach:

$keys = array_keys($arr);

$numItems = count($keys);

$i=0;

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

# Специальная обработка первого элемента происходит здесь

$i++;

while($i<$numItems-1){

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

    # Обработка обычных элементов

    $i++;

}

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

# Специальная обработка последнего элемента происходит здесь

$i++;

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

Если вы хотите функционализировать подобное поведение, то я попробовал сделать такую функцию iterateList здесь. Хотя, возможно, вы захотите сравнить код посредством инструмента «gist», если вас очень волнует эффективность.

 

Ответ 6

Использование булевой переменной по-прежнему является наиболее надежным способом, даже если вы хотите проверить первое появление $value (я нашел это более полезным в моей ситуации и во многих других ситуациях), например, так:

$is_first = true;

 foreach( $array as $value ) {

    switch ( $value ) {

        case 'match':

            echo 'appeared';

            if ( $is_first ) {

                echo 'первое появление';

                $is_first = false;

            }

            break;

        }

    }

    if( !next( $array ) ) {

        echo 'последнее появление value';

    }

}

Тогда как насчет !next( $array ) для поиска последнего $value, который вернет true, если нет следующего значения для итерации. И я предпочитаю использовать цикл for вместо foreach, если я собираюсь использовать счетчик, например, так:

$len = count( $array );

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

    $value = $array[$i];

    if ($i === 0) {

        // first

    } elseif ( $i === $len - 1 ) {

        // last

    }

    // …

    $i++;

}

 

Ответ 7

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

Текущее принятое решение использует цикл и проверку внутри цикла, которая будет выполняться каждую_итерацию. Оптимальный (быстрый) способ сделать это следующий:

$numItems = count($arr);

$i=0;

$firstitem=$arr[0];

$i++;

while($i<$numItems-1){

    $some_item=$arr[$i];

    $i++;

}

$last_item=$arr[$i];

$i++;

 Таким образом, совершенно очевидно, что проверки стоят дорого, и, конечно, чем больше проверок переменных вы добавляете, тем хуже становится.

 

 Ответ 8

Не уверен, что это по-прежнему необходимо. Но следующее решение должно работать с итераторами и не требует 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;

        }

    }

}

 

Схожие статьи

Что такое Reddit и как им пользоваться: описание сайта и регистрация
Web

Что такое Reddit и как им пользоваться: описание сайта и регистрация

Web

Тернарный оператор PHP против оператора объединения с нулевым значением

Web

Как получить имя переменной в виде строки в PHP?

Web

Почему Laravel удаляет данные связанной модели по user_id в загруженной функции модели User