Encuentre el último elemento de una matriz mientras usa un bucle foreach en PHP

215

Estoy escribiendo un creador de consultas SQL usando algunos parámetros. En Java, es muy fácil detectar el último elemento de una matriz desde el interior del bucle for simplemente comprobando la posición actual de la matriz con la longitud de la matriz.

for(int i=0; i< arr.length;i++){
     boolean isLastElem = i== (arr.length -1) ? true : false;        
}

En PHP tienen índices no enteros para acceder a las matrices. Por lo tanto, debe iterar sobre una matriz utilizando un bucle foreach. Esto se vuelve problemático cuando necesita tomar alguna decisión (en mi caso, agregar o parámetro durante la consulta).

Estoy seguro de que debe haber alguna forma estándar de hacerlo.

¿Cómo se resuelve esto en PHP?

Vaibhav Kamble
fuente
2
¿Está tratando de determinar si debe concatenar un "Y" u "O" entre partes de una cláusula where?
Darryl Hein
1
solo señala que debe almacenar el total en una variable en lugar de llamar a un método para cada iteración. para (int i = 0, int t = arr.length; i <t; i ++).
OIS
Eche un vistazo a esta solución: stackoverflow.com/a/29474468/1478566
vbarbarosh
use end (arrray) use respuestas
similares

Respuestas:

313

Parece que quieres algo como esto:

$numItems = count($arr);
$i = 0;
foreach($arr as $key=>$value) {
  if(++$i === $numItems) {
    echo "last index!";
  }
}    

Dicho esto, no tienes que iterar sobre una "matriz" usando foreachphp.

Richard Levasseur
fuente
Creo que buscaré esta solución, ya que es casi similar al código que publiqué. Incluso la respuesta de Jeremy está bien, pero creo que se hizo poco compleja en comparación con esta. No he realizado ninguna prueba, pero supongo que esta respuesta será más rápida ya que no está extrayendo un conjunto de claves. Esto tendrá una velocidad O (1)
Vaibhav Kamble
18
$numItems = count($arr)el truco no es necesario y reduce la legibilidad: en PHP no hay penalización de rendimiento por acceder al conteo ($ arr) cada vez. La razón es que el recuento de elementos se guarda internamente como un campo especial en el encabezado de la matriz y no se calcula sobre la marcha. Este truco proviene de otros lenguajes (C, Java?, ...).
johndodo
77
Eso es interesante @johndodo porque no hay penalización de rendimiento por acceder al conteo ($ arr) cada vez. ¿Tiene algún enlace / fuente para documentar esta optimización en particular? ¡Gracias!
zuallauz
2
Es bastante triste que en PHP la solución más correcta para este problema parezca poco elegante :(
kizzx2
1
@tomsihap $ i necesita incrementarse dentro del ciclo, junto con la iteración de la matriz. $ i necesita representar el número del elemento en la matriz, de modo que pueda usarse para determinar cuándo se alcanzó el último elemento. Sin ++, el bucle solo compararía "0" con el número total de elementos.
Bobby Jack
201

Puede obtener el valor de la última clave de la matriz usando end(array_keys($array))y compararlo con la clave actual:

$last_key = end(array_keys($array));
foreach ($array as $key => $value) {
    if ($key == $last_key) {
        // last element
    } else {
        // not last element
    }
}
Jeremy Ruten
fuente
2
+1 Estoy de acuerdo: las otras soluciones dependen de que la matriz tenga índices numéricos.
Patrick Glandien
19
En mi propia defensa, mi respuesta no se basa en que la matriz tenga teclas numéricas :)
Richard Levasseur
2
la comparación de cadenas es más lenta que los enteros, y no siempre es precisa al comparar cadenas con enteros (al menos debería haber usado ===). Voy a rechazar esto.
OIS
44
es elegante, pero provoca un AVISO ESTRICTO porque "fin" espera un valor de referencia :(
Wiliam
10
Solución para AVISO ESTRICTO:$lastKey = array_search(end($array), $array);
Ajax
45

¿Por qué tan complicado?

foreach($input as $key => $value) {
    $ret .= "$value";
    if (next($input)==true) $ret .= ",";
}

¡Esto agregará un, detrás de cada valor, excepto el último!

Trikks
fuente
2
No si el próximo $ input contiene un valor booleano de false, que es un problema importante con next ().
soulseekah
29
A menos que me equivoque, esto no funciona porque llamar a next () avanza el puntero de matriz, por lo que está omitiendo todos los demás elementos del bucle.
Jordan Lev
2
No parece funcionar para mí. El segundo último elemento no obtiene la coma, pero debería.
zuallauz
1
Si el valor equivale a bool false, no funciona. Tampoco imprime la última coma entre el penúltimo y el último valor.
OIS
1
Una nota para cualquiera que quiera usar esto en PHP7: el puntero de matriz no se mueve en bucles foreach, y esto no funcionará.
Scott Flack el
26

Cuando toEnd alcanza 0 significa que está en la última iteración del bucle.

$toEnd = count($arr);
foreach($arr as $key=>$value) {
  if (0 === --$toEnd) {
    echo "last index! $value";
  }
}

El último valor aún está disponible después del ciclo, por lo que si solo desea usarlo para más cosas después del ciclo, esto es mejor:

foreach($arr as $key=>$value) {
  //something
}
echo "last index! $key => $value";

Si no desea tratar el último valor como bucles internos especiales. Esto debería ser más rápido si tiene matrices grandes. (Si reutiliza la matriz después del ciclo dentro del mismo alcance, primero debe "copiar" la matriz).

//If you use this in a large global code without namespaces or functions then you can copy the array like this:
//$array = $originalArrayName; //uncomment to copy an array you may use after this loop

//end($array); $lastKey = key($array); //uncomment if you use the keys
$lastValue = array_pop($array);

//do something special with the last value here before you process all the others?
echo "Last is $lastValue", "\n";

foreach ($array as $key => $value) {
    //do something with all values before the last value
    echo "All except last value: $value", "\n";
}

//do something special with the last value here after you process all the others?
echo "Last is $lastValue", "\n";

Y para responder a su pregunta original "en mi caso, agregar o / y parámetros al crear la consulta"; esto recorrerá todos los valores, luego los unirá en una cadena con "y" entre ellos, pero no antes del primer valor o después del último valor:

$params = [];
foreach ($array as $value) {
    $params[] = doSomething($value);
}
$parameters = implode(" and ", $params);
OIS
fuente
2
Por supuesto, realizará el - $ toEnd para cada iteración, ese es el punto. Si lo moviera fuera del bucle, ya no funcionaría.
OIS
El método más simple jamás utilizado. $lastValue = array_pop($array);Gracias.
Elias Nicolas
21

Ya hay muchas respuestas, pero también vale la pena buscar iteradores, especialmente porque se le ha pedido una forma estándar:

$arr = range(1, 3);

$it = new CachingIterator(new ArrayIterator($arr));
foreach($it as $key => $value)
{
  if (!$it->hasNext()) echo 'Last:';
  echo $value, "\n";
}

Es posible que también encuentre algo que funcione más flexible para otros casos.

hakre
fuente
Gran respuesta. Le agradezco que esté utilizando las características del lenguaje destinadas a la tarea. i=0;y ++i;siempre me ha parecido hack en un lenguaje de script como PHP.
CheddarMonkey
15

Una forma podría ser detectar si el iterador tiene next. Si no hay un próximo adjunto al iterador, significa que está en el último bucle.

foreach ($some_array as $element) {
    if(!next($some_array)) {
         // This is the last $element
    }
}
Raheel
fuente
¡Creo que esta es la forma más fácil y requiere la menor cantidad de código!
hola soy zvaehn
1
No funciona con PHP 7+ como "En PHP 7, foreach no usa el puntero de matriz interno".
Damien Debin el
8

Entonces, si su matriz tiene valores de matriz únicos, entonces determinar la última iteración es trivial:

foreach($array as $element) {
    if ($element === end($array))
        echo 'LAST ELEMENT!';
}

Como puede ver, esto funciona si el último elemento aparece solo una vez en la matriz; de lo contrario, obtendrá una falsa alarma. En caso contrario, debe comparar las claves (que son únicas de seguro).

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

También tenga en cuenta el operador de copago estricto, que es bastante importante en este caso.

Rok Kralj
fuente
Esta es una manera bastante ineficiente.
Su sentido común el
No No lo es. end () realiza O (1). También es más corto que otras soluciones, y se lee bien -> Si el elemento es igual al final de la matriz, escriba "Último".
Rok Kralj
Esto es más del doble de lento que mi primer y último ejemplo para 100000 valores.
OIS
5

Suponiendo que tiene la matriz almacenada en una variable ...

foreach($array as $key=>$value) 
{ 
    echo $value;
    if($key != count($array)-1) { echo ", "; }
}
Martin Heisterkamp
fuente
Esto es muy simple y útil. Solo contaría la matriz primero fuera del bucle foreach para que el programa no tenga que contar cada vez que la función foreach evalúa cada elemento.
Pathros
3
Esto no funcionará en matrices asociativas. $ clave no siempre es un número.
Jonathan
5

para obtener el primer y el último elemento de la matriz foreach

foreach($array as $value) {
    if ($value === reset($array)) {
        echo 'FIRST ELEMENT!';
    }

    if ($value === end($array)) {
        echo 'LAST ITEM!';
    }
}
Darkcoder
fuente
4

Todavía puede usar ese método con matrices asociativas:

$keys = array_keys($array);
for ($i = 0, $l = count($array); $i < $l; ++$i) {
    $key = $array[$i];
    $value = $array[$key];
    $isLastItem = ($i == ($l - 1));
    // do stuff
}

// or this way...

$i = 0;
$l = count($array);
foreach ($array as $key => $value) {
    $isLastItem = ($i == ($l - 1));
    // do stuff
    ++$i;
}
nickf
fuente
1
Cambie $ key = $ array [$ i]; a $ clave = $ claves [$ i]; en el primero para el bucle.
Narek
4

Si necesita hacer algo para cada elemento, excepto el primero o el último y solo si hay más de un elemento en la matriz, prefiero la siguiente solución.

Sé que hay muchas soluciones anteriores y publicadas meses / un año antes que la mía, pero esto es algo que creo que es bastante elegante en sí mismo. La comprobación de cada bucle también es una comprobación booleana en lugar de una comprobación numérica "i = (cuenta-1)", que puede permitir menos gastos generales.

La estructura del bucle puede parecer incómoda, pero puede compararla con el orden de thead (principio), tfoot (final), tbody (actual) en las etiquetas de tabla HTML.

$first = true;
foreach($array as $key => $value) {
    if ($first) {
        $first = false;
        // Do what you want to do before the first element
        echo "List of key, value pairs:\n";
    } else {
        // Do what you want to do at the end of every element
        // except the last, assuming the list has more than one element
        echo "\n";
    }
    // Do what you want to do for the current element
    echo $key . ' => ' . $value;
}

Por ejemplo, en términos de desarrollo web, si desea agregar un borde inferior a cada elemento, excepto el último en una lista desordenada (ul), puede agregar un borde superior a cada elemento excepto el primero (el CSS: first-child, compatible con IE7 + y Firefox / Webkit admite esta lógica, mientras que: last-child no es compatible con IE7).

Puede sentirse libre de reutilizar la variable $ first para todos y cada uno de los bucles anidados y las cosas funcionarán bien, ya que cada bucle hace que $ first sea falso durante el primer proceso de la primera iteración (por lo que los saltos / excepciones no causarán problemas) .

$first = true;
foreach($array as $key => $subArray) {
    if ($first) {
        $string = "List of key => value array pairs:\n";
        $first = false;
    } else {
        echo "\n";
    }

    $string .= $key . '=>(';
    $first = true;
    foreach($subArray as $key => $value) {
        if ($first) {
            $first = false;
        } else {
            $string .= ', ';
        }
        $string .= $key . '=>' . $value;
    }
    $string .= ')';
}
echo $string;

Salida de ejemplo:

List of key => value array pairs:
key1=>(v1_key1=>v1_val1, v1_key2=>v1_val2)
key2=>(v2_key1=>v2_val1, v2_key2=>v2_val2, v2_key3=>v2_val3)
key3=>(v3_key1=>v3_val1)
Ankit Aggarwal
fuente
Gracias, esta es mi solución favorita! Es muy flexible y solo cuesta un booleano. Por cierto, creo que esto funcionará para una matriz que contiene al menos un elemento también (no solo más de un elemento).
jc
4

Esta debería ser la manera fácil de encontrar el último elemento:

foreach ( $array as $key => $a ) {
    if ( end( array_keys( $array ) ) == $key ) {
        echo "Last element";
     } else {
        echo "Just another element";
     }
}  

Referencia: Enlace

Dulitha Kumarasiri
fuente
- enlace roto -
T30
3

Tengo la fuerte sensación de que en la raíz de este "problema XY" el OP solo quería implode()funcionar.

Su sentido común
fuente
1
cierto. Sin embargo, hay casos en los que implosionar no es tan práctico. Imagine, por ejemplo, tratar de implosionar una larga cadena de html con muchas variables dinámicas. Claro, podría hacer un ob_start / ob_get_clean en él, o simplemente construirlo como $ str = '...'. Pero, hay momentos en que esto podría considerarse un poco exagerado
Alastair Brayne
3

Como su intención de encontrar la matriz EOF es solo para el pegamento. Conozca la siguiente táctica. No necesita requerir el EOF:

$given_array = array('column1'=>'value1',
                     'column2'=>'value2',
                     'column3'=>'value3');

$glue = '';
foreach($given_array as $column_name=>$value){
    $where .= " $glue $column_name = $value"; //appending the glue
    $glue   = 'AND';
}
echo $where;

o / p:

column1 = value1 AND column2 = value2 AND column3 = value3
Angelin Nadar
fuente
2

Parece que quieres algo como esto:

$array = array(
    'First',
    'Second',
    'Third',
    'Last'
);

foreach($array as $key => $value)
{
    if(end($array) === $value)
    {
       echo "last index!" . $value;
    }
}
Ashique CM
fuente
2
Por lo general, usar el valor no es una buena idea porque no funcionará correctamente si la matriz tiene dos valores idénticos.
orden
2

No agregue una coma después del último valor:

La matriz:

$data = ['lorem', 'ipsum', 'dolor', 'sit', 'amet'];

La función:

$result = "";
foreach($data as $value) {
    $resut .= (next($data)) ? "$value, " : $value;
}

El resultado:

print $result;

lorem, ipsum, dolor, sentarse, amet

Santo Boldižar
fuente
1

puedes hacer un conteo ().

for ($i=0;$i<count(arr);$i++){
    $i == count(arr)-1 ? true : false;
}

o si está buscando SOLO el último elemento, puede usar end ().

end(arr);

devuelve solo el último elemento.

y, como resultado, PUEDES indexar matrices php por enteros. Es perfectamente feliz con

arr[1];
holaandre
fuente
1
El inconveniente en el extremo (arr) es que establece el puntero interno de la matriz al último elemento ..
Vijay
No, NO DEBE usar números enteros para acceder a las matrices a menos que sepa que las claves son numéricas y secuenciales. Considere lo siguiente: $a = array(0=>'A', 2=>'B', 'aaa'=>'C'). ¿Qué obtienes si accedes $a[count($a)-1]?
johndodo
1

También podrías hacer algo como esto:

end( $elements );
$endKey = key($elements);
foreach ($elements as $key => $value)
{
     if ($key == $endKey) // -- this is the last item
     {
          // do something
     }

     // more code
}
KOGI
fuente
end devuelve el valor, no la matriz, por lo que la forma en que lo creó no funciona. La comparación de cadenas también es más lenta que el entero.
OIS
Tienes razón. debe ser end ($ elementos); $ endKey = key ($ elementos);
KOGI
1

Me gusta un poco lo siguiente, ya que siento que es bastante ordenado. Supongamos que estamos creando una cadena con separadores entre todos los elementos: por ejemplo, a, b, c

$first = true;
foreach ( $items as $item ) {
    $str = ($first)?$first=false:", ".$item;
}
Alastair Brayne
fuente
hazlo más simple sin declarar $ first; usa foreach ($ items como $ key => $ item) y luego $ str = ($ key == 0)?
Milan Rilex Ristic
1
foreach ($array as $key => $value) {

  $class = ( $key !== count( $array ) -1 ) ? " class='not-last'" : " class='last'";

  echo "<div{$class}>";
  echo "$value['the_title']";
  echo "</div>";

}

Referencia

Ayman Elshehawy
fuente
0

Aquí hay otra forma de hacerlo:

$arr = range(1, 10);

$end = end($arr);
reset($arr);

while( list($k, $v) = each($arr) )
{
    if( $n == $end )
    {
        echo 'last!';
    }
    else
    {
        echo sprintf('%s ', $v);
    }
}
Kevin
fuente
0

Si te entiendo, todo lo que necesitas es invertir la matriz y obtener el último elemento mediante un comando pop:

   $rev_array = array_reverse($array);

   echo array_pop($rev_array);
James
fuente
0

También puede probar esto para hacer su consulta ... que se muestra aquí con INSERT

<?php
 $week=array('one'=>'monday','two'=>'tuesday','three'=>'wednesday','four'=>'thursday','five'=>'friday','six'=>'saturday','seven'=>'sunday');
 $keys = array_keys($week);
 $string = "INSERT INTO my_table ('";
 $string .= implode("','", $keys);
 $string .= "') VALUES ('";
 $string .= implode("','", $week);
 $string .= "');";
 echo $string;
?>
marca
fuente
0

Para los scripts 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 actual aceptada utiliza un bucle y una verificación dentro del bucle que se realizará en cada_site_iteración, la forma correcta (rápida) de hacer esto 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 duran

tiempo: 3235.6359958649 milisegundos

Morg
fuente
0

Otra forma de hacerlo es recordar el resultado del ciclo de bucle anterior y usarlo como resultado final:

    $result = $where = "";
    foreach ($conditions as $col => $val) {
        $result = $where .= $this->getAdapter()->quoteInto($col.' = ?', $val);
        $where .=  " AND ";
    }
    return $this->delete($result);
Leven
fuente
0

Personalmente utilizo este tipo de construcción que permite un uso fácil con los elementos html <ul> y <li>: simplemente cambie la igualdad para otra propiedad ...

La matriz no puede contener elementos falsos, pero todos los demás elementos que se convierten en el booleano falso.

$table = array( 'a' , 'b', 'c');
$it = reset($table);
while( $it !== false ) {
    echo 'all loops';echo $it;
    $nextIt = next($table);
    if ($nextIt === false || $nextIt === $it) {
            echo 'last loop or two identical items';
    }
    $it = $nextIt;
}
MUY Bélgica
fuente
0

Puede obtener directamente el último índice de la siguiente manera:

$numItems = count($arr);

echo $arr[$numItems-1];

Denis Omeri
fuente
0
<?php foreach($have_comments as $key => $page_comment): ?>
    <?php echo $page_comment;?>
    <?php if($key+1<count($have_comments)): ?> 
        <?php echo ', '; ?>
    <?php endif;?>
<?php endforeach;?>
Zarpele
fuente
0

Aquí está mi solución: simplemente obtenga el recuento de su matriz, menos 1 (ya que comienzan en 0).

$lastkey = count($array) - 1;
foreach($array as $k=>$a){
    if($k==$lastkey){
        /*do something*/
    }
}
ITWitch
fuente