Encontre o último elemento de uma matriz enquanto usa um loop foreach no PHP

215

Estou escrevendo um criador de consultas SQL usando alguns parâmetros. Em Java, é muito fácil detectar o último elemento de uma matriz de dentro do loop for, apenas verificando a posição atual da matriz com o comprimento da matriz.

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

No PHP, eles têm índices não inteiros para acessar matrizes. Portanto, você deve iterar sobre uma matriz usando um loop foreach. Isso se torna problemático quando você precisa tomar alguma decisão (no meu caso, para adicionar ou / e parâmetro durante a criação da consulta).

Estou certo de que deve haver alguma maneira padrão de fazer isso.

Como você resolve isso em PHP?

Vaibhav Kamble
fonte
2
Você está tentando determinar se deve concorrer um "AND" ou "OR" entre partes de uma cláusula where?
Darryl Hein
1
apenas salientando que você deve armazenar o total em uma variável em vez de chamar um método para cada iteração. for (int i = 0, int t = comprimento arr.; i <t; i ++).
OIS
Dê uma olhada nesta solução: stackoverflow.com/a/29474468/1478566
vbarbarosh
use end (arrray) use respostas
semelhantes

Respostas:

313

Parece que você quer algo assim:

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

Dito isto, você não precisa iterar sobre um "array" usando foreachno php.

Richard Levasseur
fonte
Acho que vou buscar esta solução, pois é quase semelhante ao código que publiquei. Até a resposta de Jeremy é adequada, mas acho que ficou um pouco complexa em comparação com essa. Eu não corri qualquer teste, mas eu acho que essa resposta deve ser mais rápido, pois não é extrair variedade de keys.This terão O (1) Velocidade
Vaibhav Kamble
18
$numItems = count($arr)truque não é necessário e reduz a legibilidade - no PHP não há penalidade de desempenho para acessar count ($ arr) a cada vez. O motivo é que a contagem de itens é salva internamente como um campo especial no cabeçalho da matriz e não é calculada em tempo real. Esse truque vem de outras linguagens (C, Java?, ...).
Johndodo
7
Isso é interessante, @johndodo, que não há penalidade de desempenho ao acessar count ($ arr) a cada vez. Você tem links / fontes para onde essa otimização específica está documentada? Obrigado!
Zuallauz 31/01
2
É um pouco triste que no PHP a solução mais correta para este problema parece bastante deselegante :(
kizzx2
1
@tomsihap $ i precisa ser incrementado dentro do loop, em conjunto com a iteração da matriz. $ i precisa representar o número do item na matriz, para que possa ser usado para determinar quando o último item foi atingido. Sem o ++, o loop só compararia "0" com o número total de itens.
Bobby Jack
201

Você pode obter o valor da última chave da matriz usando end(array_keys($array))e compará-lo com a chave atual:

$last_key = end(array_keys($array));
foreach ($array as $key => $value) {
    if ($key == $last_key) {
        // last element
    } else {
        // not last element
    }
}
Jeremy Ruten
fonte
2
+1 Eu concordo - as outras soluções contam com a matriz com índices numéricos.
22620 Patrick Glandien
19
Na minha própria defesa, a minha resposta não depende da matriz ter teclas numéricas :)
Richard Levasseur
2
a comparação de strings é mais lenta que os números inteiros e nem sempre é precisa ao comparar strings com números inteiros (você deveria pelo menos ter usado ===). Estou votando nisso.
OIS
4
é elegante, mas causa AVISO RIGOROSA porque "fim" espera um valor de referência :(
Wiliam
10
Correção para AVISO RÍGIDO:$lastKey = array_search(end($array), $array);
Ajax
44

por que tão complicado?

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

Isso adicionará a, por trás de cada valor, exceto o último!

Trikks
fonte
2
Não se a próxima entrada $ contiver um valor booleano false, que é um grande problema com next ().
soulseekah
29
A menos que eu esteja enganado, isso não funciona porque chamar next () avança o ponteiro da matriz, então você está pulando todos os outros elementos do loop.
Jordan Lev
2
Não parece funcionar para mim. O segundo último elemento não recebe a vírgula, mas deveria.
Zuallauz 26/05/12
1
Se o valor for igual a bool false, não funcionará. Também não imprime a última vírgula entre o penúltimo e o último valor.
OIS 26/02
1
Uma observação para quem quiser usar isso no PHP7 - o ponteiro do array não se move nos loops foreach, e isso não funcionará.
Scott Flack
26

Quando toEnd atinge 0, significa que está na última iteração do loop.

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

O último valor ainda está disponível após o loop, portanto, se você quiser usá-lo para mais coisas após o loop, é melhor:

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

Se você não deseja tratar o último valor como loops internos especiais. Isso deve ser mais rápido se você tiver grandes matrizes. (Se você reutilizar a matriz após o loop dentro do mesmo escopo, precisará "copiar" a matriz primeiro).

//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 para responder à sua pergunta original "no meu caso, para adicionar ou / e parâmetro durante a criação da consulta"; isso fará um loop sobre todos os valores e juntará-os a uma string com "e" entre eles, mas não antes do primeiro valor ou após o último valor:

$params = [];
foreach ($array as $value) {
    $params[] = doSomething($value);
}
$parameters = implode(" and ", $params);
OIS
fonte
2
Obviamente, ele executará o - $ toEnd para cada iteração, esse é o ponto. Se eu o movesse para fora do loop, não funcionaria mais.
OIS
O método mais simples já usado. $lastValue = array_pop($array);Obrigado.
Elias Nicolas
21

Já existem muitas respostas, mas vale a pena examinar também os iteradores, especialmente porque foi solicitado um caminho padrão:

$arr = range(1, 3);

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

Você também pode encontrar algo que funcione mais flexível para outros casos.

hakre
fonte
Ótima resposta. Compreendo que você esteja usando os recursos do idioma que se destinam à tarefa. i=0;e ++i;sempre pareceram hackeados em uma linguagem de script como PHP.
CheddarMonkey
15

Uma maneira poderia ser detectar se o iterador possui next. Se não houver outro anexo ao iterador, significa que você está no último loop.

foreach ($some_array as $element) {
    if(!next($some_array)) {
         // This is the last $element
    }
}
Raheel
fonte
Eu acho que essa é a maneira mais fácil e requer a menor quantidade de código!
Oi im zvaehn
1
Não funciona com o PHP 7+ como "No PHP 7, o foreach não usa o ponteiro interno da matriz.".
Damien Debin
8

Portanto, se sua matriz possui valores exclusivos, determinar a última iteração é trivial:

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

Como você vê, isso funciona se o último elemento estiver aparecendo apenas uma vez na matriz, caso contrário, você receberá um alarme falso. Nesse caso, você deve comparar as chaves (que são únicas, com certeza).

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

Observe também o operador de coparisão estrito, o que é bastante importante neste caso.

Rok Kralj
fonte
Esta é uma maneira bastante ineficiente.
Seu senso comum
Não. Não é. end () executa O (1). Também é mais curto que outras soluções e lê bem -> Se o elemento é igual ao final da matriz, escreva "Last".
Ano Kralj
Isso é duas vezes mais lento que meu primeiro e último exemplo para 100000 valores.
OIS
5

Supondo que você tenha a matriz armazenada em uma variável ...

foreach($array as $key=>$value) 
{ 
    echo $value;
    if($key != count($array)-1) { echo ", "; }
}
Martin Heisterkamp
fonte
Isso é muito simples e útil. Eu contaria apenas a matriz primeiro fora do loop foreach, para que o programa não precise contar toda vez que a função foreach avaliar cada item.
Pathros
3
Isso não funcionará em matrizes associativas. A chave $ nem sempre é um número.
10136 Jonathan
5

para obter o primeiro e o último elemento da matriz foreach

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

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

Você ainda pode usar esse método com matrizes associativas:

$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
fonte
1
Por favor, altere $ key = $ array [$ i]; para $ key = $ keys [$ i]; no primeiro loop for.
Narek
4

Se você precisar fazer algo para cada elemento, exceto o primeiro ou o último e somente se houver mais de um elemento na matriz, prefiro a solução a seguir.

Sei que existem muitas soluções acima e postadas meses / um ano antes da minha, mas isso é algo que considero bastante elegante por si só. A verificação de cada loop também é uma verificação booleana, em oposição a uma verificação numérica "i = (contagem-1)", que pode permitir menos sobrecarga.

A estrutura do loop pode parecer estranha, mas você pode compará-la com a ordem de thead (início), tfoot (final), tbody (atual) nas tags da tabela 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 exemplo, em termos de desenvolvimento da web, se você deseja adicionar uma borda inferior a todos os elementos, exceto o último de uma lista não ordenada (ul), poderá adicionar uma borda superior a todos os elementos, exceto o primeiro (o CSS: o primeiro filho, suportado pelo IE7 + e Firefox / Webkit, suporta essa lógica, enquanto que: o último filho não é suportado pelo IE7).

Você pode ficar à vontade para reutilizar a variável $ first para cada loop aninhado e tudo funcionará bem, pois cada loop torna o $ first falso durante o primeiro processo da primeira iteração (para que interrupções / exceções não causem 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;

Exemplo de saída:

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
fonte
Thanx, esta é a minha solução favorita! É muito flexível e custa apenas um valor booleano. BTW, acho que isso funcionará para array contendo pelo menos um elemento também (não apenas mais de um elemento).
jc
4

Essa deve ser a maneira mais fácil de encontrar o último elemento:

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

Referência: Link

Dulitha Kumarasiri
fonte
- Link quebrado -
T30 22/03
3

Tenho uma forte sensação de que, na raiz desse "problema XY", o OP queria apenas implode()funcionar.

Seu senso comum
fonte
1
verdade. Há casos em que implodir não é tão prático. Imagine, por exemplo, tentar implodir uma longa sequência de html com muitas variáveis ​​dinâmicas. Claro, você pode fazer um ob_start / ob_get_clean nele, ou apenas construí-lo como um $ str = '...'. Mas, há momentos em que isso poderia ser considerado apenas um pouco exagerado
Alastair Brayne
3

Como sua intenção de encontrar a matriz EOF é apenas para a cola. Seja apresentado à tática abaixo. Você não precisa do 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
fonte
2

Parece que você quer algo assim:

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

foreach($array as $key => $value)
{
    if(end($array) === $value)
    {
       echo "last index!" . $value;
    }
}
Ashique CM
fonte
2
Usar o valor geralmente não é uma boa ideia, porque não funcionará corretamente se a matriz tiver dois valores idênticos.
orrd
2

Não adicione vírgula após o último valor:

A matriz:

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

A função:

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

O resultado:

print $result;

lorem, ipsum, dolor, sente-se, amet

Santo Boldižar
fonte
1

você pode fazer uma contagem ().

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

ou se você estiver procurando APENAS o último elemento, poderá usar end ().

end(arr);

retorna apenas o último elemento.

e, como se vê, você PODE indexar matrizes php por números inteiros. É perfeitamente feliz com

arr[1];
helloandre
fonte
1
A desvantagem no final (arr) é ele define ponteiro interno da matriz para o último elemento ..
Vijay
Não, você NÃO DEVE usar números inteiros para acessar as matrizes, a menos que você saiba que as teclas são numéricas e seqüenciais. Considere o seguinte: $a = array(0=>'A', 2=>'B', 'aaa'=>'C'). O que você ganha se acessar $a[count($a)-1]?
Johndodo
1

Você também pode fazer algo assim:

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

     // more code
}
KOGI
fonte
end retorna o valor, e não o array, assim como você o fez não funciona. A comparação de strings também é mais lenta que o número inteiro.
OIS
Você está certo. deve ser final (elementos $); $ endKey = chave ($ elementos);
KOGI 01/04/09
1

Eu meio que gosto do seguinte, pois acho que é bastante legal. Vamos supor que estamos criando uma string com separadores entre todos os elementos: por exemplo, a, b, c

$first = true;
foreach ( $items as $item ) {
    $str = ($first)?$first=false:", ".$item;
}
Alastair Brayne
fonte
simplifique sem declarar $ first; usar foreach ($ items como $ key => $ item) e depois $ 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>";

}

Referência

Ayman Elshehawy
fonte
0

Aqui está outra maneira de fazer isso:

$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
fonte
0

Se eu entendo você, tudo o que você precisa é reverter a matriz e obter o último elemento por um comando pop:

   $rev_array = array_reverse($array);

   echo array_pop($rev_array);
James
fonte
0

Você também pode tentar fazer essa consulta ... mostrada aqui com 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
fonte
0

Para scripts de geração de consultas SQL, ou qualquer coisa que execute uma ação diferente para o primeiro ou o último elemento, é muito mais rápido (quase o dobro da velocidade) evitar o uso de verificações desnecessárias de variáveis.

A solução atual aceita usa um loop e uma verificação dentro do loop que será feita a cada_a única_iteração, a maneira correta (rápida) de fazer isso é a seguinte:

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

Um pequeno benchmark caseiro mostrou o seguinte:

test1: 100000 execuções do modelo morg

tempo: 1869.3430423737 milissegundos

test2: 100000 execuções do modelo, se houver

tempo: 3235.6359958649 milissegundos

Morg.
fonte
0

Outro caminho a percorrer é lembrar o resultado do ciclo de loop anterior e usá-lo como resultado final:

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

Eu pessoalmente uso esse tipo de construção que permite um uso fácil com os elementos html <ul> e <li>: basta alterar a igualdade de uma outra propriedade ...

A matriz não pode conter itens falsos, mas todos os outros itens que são convertidos no 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;
}
MUY Bélgica
fonte
0

Você pode obter o último índice diretamente:

$numItems = count($arr);

echo $arr[$numItems-1];

Denis Omeri
fonte
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
fonte
0

Aqui está minha solução: Simplesmente obtenha a contagem do seu array, menos 1 (já que eles começam em 0).

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