Trova l'ultimo elemento di un array mentre usi un ciclo foreach in PHP


215

Sto scrivendo un creatore di query SQL utilizzando alcuni parametri. In Java, è molto facile rilevare l'ultimo elemento di un array dall'interno del ciclo for semplicemente controllando la posizione corrente dell'array con la lunghezza dell'array.

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

In PHP hanno indici non interi per accedere agli array. Quindi è necessario scorrere su un array usando un ciclo foreach. Ciò diventa problematico quando è necessario prendere una decisione (nel mio caso aggiungere o / e parametri durante la creazione di query).

Sono sicuro che ci deve essere un modo standard per farlo.

Come risolvi questo problema in PHP?


2
Stai tentando di determinare se devi concedere un "AND" o "OR" tra parti di una clausola where?
Darryl Hein,

1
sottolineando solo che è necessario archiviare il totale in una variabile anziché chiamare un metodo per ogni iterazione. per (int i = 0, int t = arr.length; i <t; i ++).
OIS,


Date un'occhiata a questa soluzione: stackoverflow.com/a/29474468/1478566
vbarbarosh

usa la fine (disponi) usa risposte
simili

Risposte:


313

Sembra che tu voglia qualcosa del genere:

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

Detto questo, non devi -erare su un "array" usando foreachin php.


Penso che andrò per questa soluzione in quanto è quasi simile al codice che ho pubblicato. Anche la risposta di Jeremy è adatta, ma penso che sia diventata poco complessa rispetto a questa. Non ho eseguito alcun test ma suppongo che questa risposta sarà più veloce in quanto non sta estraendo una serie di chiavi. Ciò avrà una velocità O (1)
Vaibhav Kamble

18
$numItems = count($arr)il trucco non è necessario e riduce la leggibilità: in PHP non vi è alcuna penalità per l'accesso al conteggio ($ arr) ogni volta. Il motivo è che il conteggio degli articoli viene salvato internamente come campo speciale nell'intestazione dell'array e non viene calcolato al volo. Questo trucco proviene da altre lingue (C, Java?, ...).
johndodo,

7
È interessante @johndodo che non ci sono penalità per l'accesso al conteggio ($ arr) ogni volta. Hai collegamenti / fonti in cui è documentata questa particolare ottimizzazione? Grazie!
zuallauz,

2
È piuttosto triste che in PHP la soluzione più corretta a questo problema sembri piuttosto inelegante :(
kizzx2

1
@tomsihap $ i deve essere incrementato all'interno del ciclo, in parallelo con l'iterazione dell'array. $ i deve rappresentare il numero dell'articolo nell'array, in modo che possa essere utilizzato per determinare quando è stato raggiunto l'ultimo elemento. Senza ++ il loop confronta sempre "0" con il numero totale di elementi.
Bobby Jack

201

È possibile ottenere il valore dell'ultima chiave dell'array utilizzando end(array_keys($array))e confrontandolo con la chiave corrente:

$last_key = end(array_keys($array));
foreach ($array as $key => $value) {
    if ($key == $last_key) {
        // last element
    } else {
        // not last element
    }
}

2
+1 Sono d'accordo - le altre soluzioni si basano sull'array con indici numerici.
Patrick Glandien,

19
A mia difesa, la mia risposta non si basa sull'array con tasti numerici :)
Richard Levasseur

2
il confronto delle stringhe è più lento degli interi e non sempre accurato quando si confrontano le stringhe con gli interi (avresti dovuto usare almeno ===). Sto votando questo verso il basso.
OIS,

4
è elegante, ma causa AVVISO STRETTO perché "end" prevede un valore di riferimento :(
Wiliam

10
Correzione per AVVISO STRETTO:$lastKey = array_search(end($array), $array);
Ajax

45

perché così complicato?

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

Questo aggiungerà un, dietro ogni valore tranne l'ultimo!


2
Non se il prossimo input $ contiene un valore booleano false, che è un grosso problema con next ().
Soulseekah,

29
A meno che non mi sbagli, questo non funziona perché chiamando next () fa avanzare il puntatore dell'array, quindi stai saltando tutti gli altri elementi nel ciclo.
Jordan Lev,

2
Non sembra funzionare per me. Il secondo ultimo elemento non ottiene la virgola ma dovrebbe.
zuallauz,

1
Se il valore equivale a bool falso, non funziona. Inoltre, non stampa l'ultima virgola tra il penultimo e l'ultimo valore.
OIS,

1
Una nota per chiunque voglia usarlo in PHP7: il puntatore dell'array non si sposta nei cicli foreach e questo non funzionerà.
Scott Flack,

26

Quando toEnd raggiunge 0 significa che è all'ultima iterazione del ciclo.

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

L'ultimo valore è ancora disponibile dopo il ciclo, quindi se vuoi semplicemente usarlo per più cose dopo il ciclo, è meglio:

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

Se non si desidera trattare l'ultimo valore come speciali loop interni. Questo dovrebbe essere più veloce se si dispone di array di grandi dimensioni. (Se riutilizzi l'array dopo il loop all'interno dello stesso ambito, devi prima "copiare" l'array).

//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";

E per rispondere alla tua domanda originale "nel mio caso di aggiungere o / e parametro durante la creazione di query"; questo eseguirà il ciclo su tutti i valori, quindi li unirà insieme a una stringa con "e" tra loro ma non prima del primo valore o dopo l'ultimo valore:

$params = [];
foreach ($array as $value) {
    $params[] = doSomething($value);
}
$parameters = implode(" and ", $params);

2
Ovviamente eseguirà - $ toEnd per ogni iterazione, questo è il punto. Se lo spostassi fuori dal ciclo, non funzionerebbe più.
OIS,

Il metodo più semplice mai usato. $lastValue = array_pop($array);Grazie.
Elias Nicolas,

21

Esistono già molte risposte, ma vale anche la pena esaminare gli iteratori, soprattutto perché è stato chiesto un modo standard:

$arr = range(1, 3);

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

Potresti trovare qualcosa che funzioni in modo più flessibile anche per altri casi.


Bella risposta. Apprezzo che tu stia utilizzando le funzionalità del linguaggio previste per l'attività. i=0;e ++i;sono sempre sembrato hacker in un linguaggio di scripting come PHP.
CheddarMonkey,

15

Un modo potrebbe essere quello di rilevare se l'iteratore ha next. Se non c'è nessun prossimo attaccato all'iteratore significa che sei nell'ultimo ciclo.

foreach ($some_array as $element) {
    if(!next($some_array)) {
         // This is the last $element
    }
}

Penso che questo sia il modo più semplice e richieda la minor quantità di codice!
ciao im zvaehn,

1
Non funziona con PHP 7+ come "In PHP 7, foreach non utilizza il puntatore di array interno".
Damien Debin,

8

Quindi, se l'array ha valori di array univoci, determinare l'ultima iterazione è banale:

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

Come vedi, questo funziona se l'ultimo elemento appare solo una volta nella matrice, altrimenti si ottiene un falso allarme. In caso contrario, devi confrontare le chiavi (che sono univoche di sicuro).

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

Si noti inoltre il rigoroso operatore di coparisione, che è abbastanza importante in questo caso.


questo è un modo abbastanza inefficiente.
Il tuo buon senso

No. Non è. end () esegue O (1). È anche più corto di altre soluzioni e legge bene -> Se l'elemento è uguale alla fine dell'array, scrivi "Last".
Rok Kralj,

Questo è più del doppio del mio primo e ultimo esempio per 100000 valori.
OIS,

5

Supponendo che l'array sia archiviato in una variabile ...

foreach($array as $key=>$value) 
{ 
    echo $value;
    if($key != count($array)-1) { echo ", "; }
}

Questo è molto semplice e utile. Vorrei solo contare l'array prima all'esterno del ciclo foreach in modo che il programma non debba contare ogni volta che la funzione foreach valuta ogni elemento.
Pathros,

3
Questo non funzionerà su array associativi. La chiave $ non è sempre un numero.
Jonathan,

5

per ottenere il primo e l'ultimo elemento dall'array foreach

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

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

4

È ancora possibile utilizzare quel metodo con array associativi:

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

1
Modifica $ key = $ array [$ i]; a $ key = $ keys [$ i]; nel primo per loop.
Narek,

4

Se devi fare qualcosa per ogni elemento tranne il primo o l'ultimo e solo se nell'array è presente più di un elemento, preferisco la seguente soluzione.

So che ci sono molte soluzioni sopra e pubblicate mesi / un anno prima della mia, ma questo è qualcosa che ritengo abbastanza elegante a sé stante. Il controllo di ogni ciclo è anche un controllo booleano rispetto a un controllo numerico "i = (count-1)", che può consentire un minor sovraccarico.

La struttura del loop può sembrare scomoda, ma puoi confrontarla con l'ordinamento di thead (inizio), tfoot (fine), tbody (corrente) nei tag della tabella 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;
}

Ad esempio, in termini di sviluppo Web, se si desidera aggiungere un bordo inferiore a ogni elemento tranne l'ultimo in un elenco non ordinato (ul), è possibile invece aggiungere un bordo superiore a ogni elemento tranne il primo (CSS: first-child, supportato da IE7 + e Firefox / Webkit supporta questa logica, mentre: last-child non è supportato da IE7).

Puoi sentirti libero di riutilizzare anche la prima variabile $ per ogni ciclo nidificato e le cose funzioneranno bene poiché ogni ciclo rende $ first falso durante il primo processo della prima iterazione (quindi interruzioni / eccezioni non causeranno problemi) .

$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;

Esempio di output:

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)

Grazie, questa è la mia soluzione preferita! È molto flessibile e costa solo un valore booleano. A proposito, penso che funzionerà anche per array contenenti almeno un elemento (non solo più di un elemento).
jc,

4

Questo dovrebbe essere il modo più semplice per trovare l'ultimo elemento:

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

Riferimento: Link


- collegamento interrotto -
T30

3

Ho la netta sensazione che alla radice di questo "problema XY" l'OP volesse semplicemente implode()funzionare.


1
vero. Ci sono casi in cui implodere non è altrettanto pratico. Immagina, ad esempio, di provare a implodere una lunga stringa di html con molte variabili dinamiche. Certo, potresti fare un ob_start / ob_get_clean su di esso, o semplicemente costruirlo come $ str = '...'. Ma ci sono momenti in cui questo potrebbe essere considerato solo un po 'eccessivo
Alastair Brayne

3

Poiché la tua intenzione di trovare l'array EOF è solo per la colla. Fatti conoscere la tattica di seguito. Non è necessario richiedere l'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;

operazione:

column1 = value1 AND column2 = value2 AND column3 = value3


2

Sembra che tu voglia qualcosa del genere:

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

foreach($array as $key => $value)
{
    if(end($array) === $value)
    {
       echo "last index!" . $value;
    }
}

2
L'uso del valore di solito non è una buona idea perché non funzionerà correttamente se l'array ha due valori identici.
or

2

Non aggiungere una virgola dopo l'ultimo valore:

La matrice:

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

La funzione:

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

Il risultato:

print $result;

Lorem ipsum dolor sit amet


1

puoi fare un conteggio ().

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

o se stai cercando SOLO l'ultimo elemento, puoi usare end ().

end(arr);

restituisce solo l'ultimo elemento.

e, a quanto pare, PUOI indicizzare matrici php per numeri interi. È perfettamente soddisfatto

arr[1];

1
Lo svantaggio alla fine (arr) è che imposta il puntatore interno dell'array sull'ultimo elemento ..
Vijay,

No, NON DOVETE usare numeri interi per accedere agli array a meno che non sappiate che i tasti sono numerici e sequenziali. Prendere in considerazione: $a = array(0=>'A', 2=>'B', 'aaa'=>'C'). Cosa ottieni se accedi $a[count($a)-1]?
johndodo,

1

Potresti anche fare qualcosa del genere:

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

     // more code
}

end restituisce il valore non l'array, quindi il modo in cui è stato creato non funziona. il confronto delle stringhe è anche più lento dell'intero.
OIS,

Hai ragione. dovrebbe essere fine ($ elementi); $ endKey = key ($ elements);
KOGI

1

In un certo senso mi piace quanto segue perché mi sembra abbastanza pulito. Supponiamo di creare una stringa con separatori tra tutti gli elementi: ad esempio a, b, c

$first = true;
foreach ( $items as $item ) {
    $str = ($first)?$first=false:", ".$item;
}

renderlo più semplice senza dichiarare $ first; usa foreach ($ items come $ key => $ item) quindi $ 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>";

}

Riferimento


0

Ecco un altro modo per farlo:

$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);
    }
}

0

Se ti capisco, tutto ciò che serve è invertire l'array e ottenere l'ultimo elemento con un comando pop:

   $rev_array = array_reverse($array);

   echo array_pop($rev_array);

0

Puoi anche provare questo per rendere la tua query ... mostrata qui 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;
?>

0

Per le query SQL che generano script o tutto ciò che compie un'azione diversa per il primo o l'ultimo elemento, è molto più veloce (quasi due volte più veloce) per evitare l'uso di controlli variabili non necessari.

La soluzione attualmente accettata utilizza un ciclo e un controllo all'interno del ciclo che verrà eseguito every_single_iteration, il modo corretto (veloce) per farlo è il seguente:

$numItems = count($arr);
$i=0;
$firstitem=$arr[0];
$i++;
while($i<$numItems-1){
    $some_item=$arr[$i];
    $i++;
}
$last_item=$arr[$i];
$i++;

Un piccolo benchmark fatto in casa ha mostrato quanto segue:

test1: 100000 serie di modelli morg

tempo: 1869.3430423737 millisecondi

test2: 100000 esecuzioni del modello se ultimo

tempo: 3235.6359958649 millisecondi


0

Un altro modo di procedere è ricordare il risultato del ciclo precedente e usarlo come risultato finale:

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

0

Personalmente uso questo tipo di costruzione che consente un facile utilizzo con gli elementi html <ul> e <li>: basta cambiare l'uguaglianza per un'altra proprietà ...

L'array non può contenere elementi falsi ma tutti gli altri elementi che vengono lanciati nel falso booleano.

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

0

Puoi ottenere direttamente l'ultimo indice per:

$numItems = count($arr);

echo $arr[$numItems-1];


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;?>

0

Ecco la mia soluzione: è sufficiente ottenere il conteggio dell'array, meno 1 (poiché iniziano in 0).

$lastkey = count($array) - 1;
foreach($array as $k=>$a){
    if($k==$lastkey){
        /*do something*/
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.