php - recorrer - ¿Cómo determinar la primera y la última iteración en un bucle foreach?




recorrer array asociativo php (12)

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

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.


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

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++;
}

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

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 de use , 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
  }
}




foreach