multidimensional - php foreach examples
¿Cómo determinar la primera y la última iteración en un bucle foreach? (18)
1: ¿Por qué no usar una simple declaración? Suponiendo que esté utilizando una matriz real y no un Iterator
, podría comprobar fácilmente si la variable del contador es 0 o una menos que la cantidad total de elementos. En mi opinión esta es la solución más limpia y comprensible ...
$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: Debería considerar el uso de Conjuntos anidados para almacenar su estructura de árbol. Además, puedes mejorar todo usando funciones recursivas.
La pregunta es simple. Tengo un bucle foreach
en mi código:
foreach($array as $element) {
//code
}
En este bucle, quiero reaccionar de manera diferente cuando estamos en la primera o la última iteración.
¿Como hacer esto?
Con claves y valores, esto también funciona:
foreach ($array as $key => $value) {
if ($value === end($array)) {
echo "LAST ELEMENT!";
}
}
El uso de una variable booleana sigue siendo el más confiable, incluso si desea verificar la primera aparición de un $value
(lo encontré más útil en mi situación y en muchas situaciones) , como esta:
$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'';
}
}
Entonces, ¿qué tal !next( $array )
para encontrar el último $value
que devolverá true
si no hay ningún valor next()
para iterar.
Y prefiero usar un bucle for
lugar de foreach
si fuera a usar un contador, como este:
$len = count( $array );
for ( $i = 0; $i < $len; $i++ ) {
$value = $array[$i];
if ($i === 0) {
// first
} elseif ( $i === $len - 1 ) {
// last
}
// …
$i++;
}
La mejor respuesta:
$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";
}
La respuesta más eficiente de @morg, a diferencia de foreach
, solo funciona para arreglos adecuados, no para objetos hash map. Esta respuesta evita la sobrecarga de una declaración condicional para cada iteración del bucle, como en la mayoría de estas respuestas (incluida la respuesta aceptada) mediante el manejo específico del primer y último elemento, y el bucle sobre los elementos centrales.
La función array_keys
se puede usar para hacer que la respuesta eficiente funcione como 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++;
No he realizado una evaluación comparativa de esto, pero no se ha agregado ninguna lógica al bucle, que es el mayor impacto para el rendimiento, por lo que sospecho que las pruebas de referencia proporcionadas con la respuesta eficiente son bastante cercanas.
Si deseaba funcionalizar este tipo de cosas, aquí me he dado un giro en una función de lista iterativa . Sin embargo, es posible que desee comparar el código global si está muy preocupado por la eficiencia. No estoy seguro de cuánta sobrecarga introduce la invocación de la función.
Me encontré con este hilo cuando tengo el mismo problema. Solo necesito obtener el primer elemento, luego re-analizo mi código hasta que esto vino a mi mente.
$firstElement = true;
foreach ($reportData->result() as $row)
{
if($firstElement) { echo "first element"; $firstElement=false; }
// Other lines of codes here
}
Los códigos anteriores son excelentes y completos, pero si solo necesita el primer elemento, puede probar este código.
No estoy seguro si aún es necesario. Pero la siguiente solución debería funcionar con iteradores y no requiere 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;
}
}
}
Para encontrar el último artículo, encuentro que cada pieza de código funciona cada vez:
foreach( $items as $item ) {
if( !next( $items ) ) {
echo ''Last Item'';
}
}
Para las secuencias de comandos de generación de consultas SQL, o cualquier cosa que realice una acción diferente para el primer o el último elemento, es mucho más rápido (casi el doble de rápido) evitar el uso de comprobaciones de variables innecesarias.
La solución aceptada actual utiliza un bucle y una verificación dentro del bucle que se realizará de forma simple, la forma correcta (rápida) de hacerlo es la siguiente:
$numItems = count($arr);
$i=0;
$firstitem=$arr[0];
$i++;
while($i<$numItems-1){
$some_item=$arr[$i];
$i++;
}
$last_item=$arr[$i];
$i++;
Un pequeño punto de referencia casero mostró lo siguiente:
test1: 100000 ejecuciones del modelo morg
tiempo: 1869.3430423737 milisegundos
test2: 100000 ejecuciones de modelo si último
tiempo: 3235.6359958649 milisegundos
Y, por lo tanto, es bastante claro que el cheque cuesta mucho y, por supuesto, empeora a medida que se agregan más cheques variables;)
Podría eliminar el primer y último elemento de la matriz y procesarlos por separado.
Me gusta esto:
<?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
?>
Eliminar todo el formato a CSS en lugar de etiquetas en línea mejoraría su código y aceleraría el tiempo de carga.
También puedes evitar mezclar HTML con la lógica php siempre que sea posible.
Su página podría ser mucho más legible y mantenible separando cosas como esta:
<?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
}
?>
Podrías usar un contador:
$i = 0;
$len = count($array);
foreach ($array as $item) {
if ($i == 0) {
// first
} else if ($i == $len - 1) {
// last
}
// …
$i++;
}
Prueba esto:
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);
Puedes usar el contador y la longitud de la matriz.
$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++; }
Si prefiere una solución que no requiera la inicialización del contador fuera del bucle, propongo comparar la clave de iteración actual con la función que le indica la última / primera clave de la matriz.
Esto se vuelve algo más eficiente (y más legible) con el próximo PHP 7.3.
Solución para PHP 7.3 y superior:
foreach($array as $key => $element) {
if ($key === array_key_first($array))
echo ''FIRST ELEMENT!'';
if ($key === array_key_last($array))
echo ''LAST ELEMENT!'';
}
Solución para todas las versiones de PHP:
foreach($array as $key => $element) {
reset($array);
if ($key === key($array))
echo ''FIRST ELEMENT!'';
end($array);
if ($key === key($array))
echo ''LAST ELEMENT!'';
}
Simplemente esto funciona!
//Store the last key
$lastkey = key(end($array));
foreach($array as $key => $element) {
....do array stuff
if ($lastkey === key($array))
echo ''LAST ELEMENT!'';
}
También puedes usar una función anónima:
$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
}
});
Tres cosas más deben ser mencionadas:
- Si su matriz no está indexada estrictamente (numéricamente) primero debe canalizar su matriz a través de
array_values
. - Si necesita modificar el
$element
, debe pasarlo por referencia (&$element
). - Cualquier variable fuera de la función anónima que necesite dentro, tendrá que enumerarlas junto a
$indexOfLastElement
dentro de la construcción deuse
, nuevamente por referencia si es necesario.
Un intento de encontrar el primero sería:
$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
}
}
Una versión más simplificada de lo anterior y suponiendo que no está utilizando índices personalizados ...
$len = count($array);
foreach ($array as $index => $item) {
if ($index == 0) {
// first
} else if ($index == $len - 1) {
// last
}
}
Versión 2 - Porque he venido a odiar el uso de la otra cosa a menos que sea necesario.
$len = count($array);
foreach ($array as $index => $item) {
if ($index == 0) {
// first
// do something
continue;
}
if ($index == $len - 1) {
// last
// do something
continue;
}
}