Вопрос простой. В моем коде есть цикл 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;
}
}
}
Web