Bir foreach döngüsünde ilk ve son yineleme nasıl belirlenir?


494

Soru basit. foreachKodumda bir döngü var :

foreach($array as $element) {
    //code
}

Bu döngüde, ilk veya son yinelemede olduğumuzda farklı tepki vermek istiyorum.

Bu nasıl yapılır?

Yanıtlar:


426

Bir sayaç kullanabilirsiniz:

$i = 0;
$len = count($array);
foreach ($array as $item) {
    if ($i == 0) {
        // first
    } else if ($i == $len - 1) {
        // last
    }
    // …
    $i++;
}

24
Ben de aşağı doğru oylama burada sanmıyorum çünkü bu da düzgün çalışıyor ve hala kullanmak kadar çöp değil array_shiftve array_pop. Böyle bir şeyi uygulamak zorunda kalsaydım ortaya çıkan çözüm bu olsa da, şimdi Rok Kralj'ın cevabına sadık kalacağım .
shadyyx

15
Bir sayaca ihtiyacım varsa, FOREACH yerine FOR döngüsünü kullanmayı tercih ederim.
rkawano

10
Eğer kullanırsan $i = 1, endişelenmene gerek yok $len - 1, sadece kullan $len.
aksu

2
@Twan 3. nokta nasıl doğrudur? Yoksa HTML içerdiğinden bu soruya hiç önem vermedi mi? Bu bir PHP sorusu, açıkça ... ve işaretleme semantiği söz konusu olduğunda, "uygun bir blahblah her zaman blahblah'tan daha iyidir (bu bile benim görüşüm değil, saf gerçektir)"
Deji

1
@rkawano ama FOR döngüsünü kullanırsanız adlandırılmış anahtarı alamazsınız
Fahmi

1015

Sayacın döngü dışında başlatılmasını gerektirmeyen bir çözümü tercih ederseniz, geçerli yineleme anahtarını size dizinin son / ilk anahtarını söyleyen işlevle karşılaştırmayı öneririm.

Bu, yaklaşan PHP 7.3 ile biraz daha verimli (ve daha okunabilir) hale geliyor.

PHP 7.3 ve üstü için çözüm:

foreach($array as $key => $element) {
    if ($key === array_key_first($array))
        echo 'FIRST ELEMENT!';

    if ($key === array_key_last($array))
        echo 'LAST ELEMENT!';
}

Tüm PHP sürümleri için çözüm:

foreach($array as $key => $element) {
    reset($array);
    if ($key === key($array))
        echo 'FIRST ELEMENT!';

    end($array);
    if ($key === key($array))
        echo 'LAST ELEMENT!';
}

44
Harika cevap! Bir dizi dizi kullanmaktan çok daha temiz.
Paul J

14
Bu en tepeye kadar patlamalıdır çünkü doğru cevaptır. Bu işlevlerin array_shift ve array_pop kullanımına göre bir diğer avantajı, daha sonraki bir noktada ihtiyaç duyulması durumunda dizilerin bozulmadan kalmasıdır. Sadece bilgi için + 1.
Awemo

13
kodunuzu temiz tutmak istiyorsanız bu kesinlikle en iyi yoldur. Onu yok etmek üzereydim, ama şimdi bu dizi yöntemlerinin fonksiyonel yükünün buna değdiğine ikna olmadım. Sadece son elemandan bahsediyorsak , bu döngüdeki her yinelemenin üzerinde end()+ dır key()- eğer ikisi de ise her seferinde 4 yöntem denir. Bu çok hafif işlemler olacak ve muhtemelen sadece işaretçi aramaları olacak, ancak dokümanlar bunu belirtmek reset()ve dizinin dahili işaretçisini end() değiştirmek için devam ediyor - bu yüzden bir sayaçtan daha mı hızlı? muhtemelen hayır.
pospi

19
Bir foreach içinde sıfırlama ($ dizi) sorunu gerektiğini sanmıyorum. Resmi belgelerden (www.php.net/foreach): "Foreach, dahili dizi işaretçisine döngü içinde değiştiğinden, bunu döngü içinde değiştirmek beklenmedik davranışlara yol açabilir." Ve sıfırlama tam olarak bunu yapar (www.php.net/reset): "Bir dizinin iç göstergesini ilk elemanına ayarla"
Gonçalo Queirós

11
@ GonçaloQueirós: Çalışıyor. Foreach dizinin bir kopyası üzerinde çalışır. Bununla birlikte, yine de endişe duyuyorsanız, reset()çağrıyı foreach'ten önce taşıyın ve sonucu önbelleğe alın $first.
Rok Kralj

121

Son öğeyi bulmak için, bu kod parçasının her seferinde işe yaradığını görüyorum:

foreach( $items as $item ) {
    if( !next( $items ) ) {
        echo 'Last Item';
    }
}

2
Bunun çok az sayıda oyu var, bunu kullanmanın bir sakıncası var mı?
Kevin Kuyl


2
@Kevin Kuyl - Yukarıdaki Pang tarafından belirtildiği gibi, dizi PHP'nin yanlış olarak değerlendirdiği bir öğe içeriyorsa (yani 0, "", null) bu yöntem beklenmedik sonuçlara yol açar. ===
Eric Kigathi

4
Bu çoğunlukla çok harika ama başkalarına işaret gibi sorunu açıklığa kavuşturmak için her zaman gibi bir dizi başarısız olacaktır [true,true,false,true]. Ama şahsen ben boolean içermeyen bir dizi ile ilgileniyorum zaman kullanacağım false.
billynoah

4
next()gereken hiçbir zaman bir foreach döngü içinde kullanılabilir. Dahili dizi işaretçisini karıştırır. Daha fazla bilgi için belgelere bakın.
Drazzah

89

Yukarıdakilerin daha basitleştirilmiş bir sürümü ve özel dizinler kullanmadığınız varsayılıyor ...

$len = count($array);
foreach ($array as $index => $item) {
    if ($index == 0) {
        // first
    } else if ($index == $len - 1) {
        // last
    }
}

Sürüm 2 - Çünkü gerekmedikçe başkalarını kullanarak tiksinmeye geldim.

$len = count($array);
foreach ($array as $index => $item) {
    if ($index == 0) {
        // first
        // do something
        continue;
    }

    if ($index == $len - 1) {
        // last
        // do something
        continue;
    }
}

8
Bu, nesneler için de geçerlidir. Diğer çözümler sadece diziler için çalışır.
Lamy

1
Bu benim için en iyi cevap ama yoğun olmalı, foreach döngüsü dışında uzunluk bildiren bir nokta yok: if ($ index == count ($ array) -1) {...}
Andrew

2
@Bu şekilde, her yineleme için dizinin öğelerini saymaya devam edersiniz.
pcarvalho

1
@peteroak Evet aslında teknik olarak performansı ve sayımınızın ne kadar veya döngülerin önemli olabileceğine bağlı olarak zarar verir. Yorumumu dikkate almayın: D
Andrew

4
@peteroak @Andrew Bir dizideki toplam öğe sayısı dahili olarak bir özellik olarak saklanır, dolayısıyla hiçbir performans isabeti yapılmaz if ($index == count($array) - 1). Buraya bakın .
GreeKatrina

36

Dizideki ilk ve son öğeleri kaldırabilir ve ayrı ayrı işleyebilirsiniz.

Bunun gibi:

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

Satır içi etiketler yerine tüm biçimlendirmeyi kaldırmak, kodunuzu iyileştirir ve yükleme süresini hızlandırır.

Mümkün olduğunda HTML'yi php logic ile karıştırmaktan da kaçınabilirsiniz.

Sayfanız, bunun gibi şeyleri ayırarak çok daha okunabilir ve sürdürülebilir hale getirilebilir:

<?php
function create_menu($params) {
  //retrieve 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
}
?>

20

İlkini bulma girişimi:

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

3
Kodunuzun nasıl çalıştığı ve OP'nin sorununu nasıl çözdüğüne ilişkin bir açıklama eklemek için lütfen yanıtınızı düzenleyin. Birçok SO afişi yenidir ve gönderdiğiniz kodu anlamaz.
i yabancı alarma

4
Bu yanıt, döngünün son yinelemesinde olup olmadığınızı nasıl belirleyeceğinizi söylemez. Ancak, cevap için geçerli bir girişimdir ve bir cevap olarak işaretlenmemelidir. Eğer hoşunuza gitmiyorsa, oylamalısınız, işaretlememelisiniz.
ArtOfWarfare

Açıktır, ilk yinelemede ilk koşula girecek ve sonra değerini yanlış olarak değiştirecek ve bu şekilde sadece bir kez ilk yinelemeye girecektir.
Mohamed23gharbi

20

Basitçe bu işe yarıyor!

// Set the array pointer to the last key
end($array);
// Store the last key
$lastkey = key($array);  
foreach($array as $key => $element) {
    ....do array stuff
    if ($lastkey === key($array))
        echo 'THE LAST ELEMENT! '.$array[$lastkey];
}

Son sorunu çözdüğünüz için @billynoah'a teşekkür ederiz .


3
En iyi! Sadece açıklığa kavuşurdum if ($key === $lastkey).
Krzysztof Przygoda

2
bu olmamalı if ($lastkey === $key)mı?
Kinetic

1
Anladım:PHP Warning: key() expects parameter 1 to be array, integer given in php shell code on line 1
billynoah

1
@Sydwell - hatayı okuyun. key()tamsayı alıyor, değil end(). end()" son elemanın değerini döndürür " ve key()bir dizi girdi olarak bekler.
billynoah


11

1: Neden basit bir forifade kullanmıyorsunuz ? Gerçek bir dizi kullandığınızı varsayarsak Iterator, sayaç değişkeninin 0 veya tüm eleman sayısından daha az olup olmadığını kolayca kontrol edebilirsiniz. Bence bu en temiz ve anlaşılır çözüm ...

$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: Ağaç yapınızı saklamak için İç İçe Kümeler kullanmayı düşünmelisiniz . Ayrıca, özyinelemeli işlevler kullanarak her şeyi iyileştirebilirsiniz.


Eğer bir kullanacaksanız , vücudunuzdan diğerine forgeçebilir ve s'yi alabilirsiniz . Tekrar tekrar kontrol etmenin bir anlamı yok. 1n-1if
29'da mpen

9

En iyi cevap:

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

}

2
Dizide yalnızca bir öğe olduğunda bu başarısız olur.
Memochipan

4
Dizide her zaman birden fazla çimento olduğundan emin olabildiğiniz sürece hızlı ve kolaydır (Memochipan'ın dediği gibi). Bu benim için başarısız bir çözüm değil - 'en iyi cevap' yok.
Seika85

5
her () PHP 7.2.0'dan itibaren DEPRECATED olacaktır . Lütfen ayrıca php.net/manual/tr/function.each.php adresini
Flow

8

Morg'un en etkili cevabı , aksine foreach, sadece harita dizileri için çalışır, karma harita nesneleri için değil. Bu yanıt , ilk ve son öğeyi özel olarak işleyerek ve orta elemanlar üzerinde döngü yaparak bu yanıtların çoğunda (kabul edilen cevap dahil) olduğu gibi, döngünün her yinelemesi için koşullu bir ifadenin ek yükünü önler .

array_keysFonksiyonu gibi verimli cevap çalışması için kullanılabilir 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++;

Bu konuda bir kıyaslama yapmadım, ancak döngüye hiçbir mantık eklenmedi, bu da performansa en büyük hit oldu, bu yüzden verimli cevapla sağlanan kriterlerin oldukça yakın olduğundan şüpheleniyorum.

Eğer isteseydim işlevselleştirmek bu tür şeyleri, böyle bir bir yumruk attık burada iterateList fonksiyonu . Bununla birlikte, verimlilik konusunda süper endişeniz varsa, gist kodunu karşılaştırmak isteyebilirsiniz. Tüm fonksiyon çağrısının ne kadar ek yük getirdiğinden emin değilim.


6

SQL sorgusu üreten komut dosyaları veya ilk veya son öğeler için farklı bir işlem yapan herhangi bir şey için, gereksiz değişken denetimlerinin kullanılmasını önlemek çok daha hızlıdır (neredeyse iki kat daha hızlıdır).

Kabul edilen geçerli çözüm, döngü içinde bir döngü ve her_single_iteration yapılacak bir denetim kullanır, bunu yapmanın doğru (hızlı) yolu aşağıdaki gibidir:

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

Küçük bir ev yapımı kriter aşağıdakileri gösterdi:

test1: model morg 100000 çalışır

zaman: 1869.3430423737 milisaniye

test2: 100000 model son çalışırsa

zaman: 3235.6359958649 milisaniye

Ve böylece çekin çok maliyetli olduğu ve tabii ki eklediğiniz daha değişken çeklerin daha da kötüleştiği açıktır;)


Kodunuz yalnızca artan tam sayı anahtarlarına sahip olduğunuzdan emin olmanız durumunda çalışır. $arr = array('one' => "1 1 1", 4 => 'Four', 1 => 'One'); $numItems = count($arr); $i=0; $firstitem=$arr[0]; echo $i . ': ' . $firstitem . ", "; $i++; while($i<$numItems-1){ $some_item=$arr[$i]; echo $i . ': ' . $some_item . ", "; $i++; } $last_item=$arr[$i]; echo $i . ': ' . $last_item . ", "; $i++;çıktı:0: , 1: One, 2: ,
Seika85

bir karma harita bir dizi döküm tanımlanamayan bir davranış, "Nesne" array()yapılır {'one':"1 1 1",0:"",1:"One",2:"",3:"",4:"Four"}ama boş öğeler sayım ile yok sayılır, tanımlanmış "şeyler" sayısını sayıyor !! BOŞLUK KURULUŞU GELİYOR! Bu cevap lütfu hak ediyor, ama eğer @Morg. gitti, anlamsız. Muhtemelen tekrar kullanmayacak bir kişiye lütuf verecek! Eğer geri gelir ve cevabını iyileştirirse, ödülünü hak eder!
mjz19910

@ Mjz19910'un belirttiği gibi, karma haritalar ve diziler birbirinin yerine kullanılamaz. Ancak, birlikte karma özelliklerini alabilirsiniz array_keysişleve, olabilir bir dizi gibi davranın. Bkz benim "geliştirilmiş" cevabını .
TheMadDeveloper

Ben sorgu için kullandığım şey budur:$querySet = ""; foreach ($fieldSet as $key=>$value) { $value = $db->dbLink->quote($value); $querySet .= "$key = $value, "; } $querySet = substr_replace($querySet, "", -2); $queryString = "UPDATE users SET $querySet WHERE user_ID = '$user_ID'";
Rovshan Mamedov

5

Anahtarlar ve Değerler ile bu da işe yarar:

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

2
Bu şekilde değerleri karşılaştırırsınız ve bir dizi iki öğe içeriyorsa çalışmaz.
Krzysztof Przygoda

5

Bir Boolean değişkeni kullanmak hala en güvenilir olanıdır, hatta ilk görünümünü kontrol etmek isteseniz bile $value (bunu benim durumumda ve birçok durumda daha kullanışlı buldum) , şöyle:

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

Sonra yinelenecek bir değer yoksa geri dönecek !next( $array )son bulmak nasıl .$valuetruenext()

Ve ben bir sayaç kullanacaksam foryerine bir döngü kullanmayı tercih ederim foreach, şöyle:

$len = count( $array );
for ( $i = 0; $i < $len; $i++ ) {
    $value = $array[$i];
    if ($i === 0) {
        // first
    } elseif ( $i === $len - 1 ) {
        // last
    }
    // …
    $i++;
}

4

Aynı sorun olduğunda ben bu iş parçacığı rastladı. Sadece ilk eleman almak gerekir sonra bu aklıma kadar gelene kadar kodumu yeniden analiz.

$firstElement = true;

foreach ($reportData->result() as $row) 
{
       if($firstElement) { echo "first element"; $firstElement=false; }
       // Other lines of codes here
}

Yukarıdaki kodlar harika ve eksiksiz ancak yalnızca ilk öğeye ihtiyacınız varsa bu kodu deneyebilirsiniz.


2

Hala gerekli olup olmadığından emin değilim. Ancak aşağıdaki çözüm yineleyicilerle çalışmalıdır ve gerektirmez 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;
        }
    }
}

0

Anonim bir işlevi de kullanabilirsiniz:

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

Üç şeyden daha bahsetmek gerekir:

  • Diziniz kesinlikle (sayısal olarak) dizine eklenmemişse, array_valuesönce dizinizi oluşturmalısınız .
  • Eğer değiştirmeniz gerekiyorsa $elementreferans ( &$element) ile geçmeniz gerekir .
  • İhtiyacınız olan anonim fonksiyonun dışından gelen değişkenler, gerekirse yapıyı referans $indexOfLastElementalarak bunları useyapının içinde listelemeniz gerekir.

0

Sayacı ve dizi uzunluğunu kullanabilirsiniz.

    $ dizi = dizi (1,2,3,4);

    $ i = 0;
    $ len = sayım ($ dizi);
    foreach ($ dizi olarak $ item) {
        eğer ($ i === 0) {
            // ilk
        } else if ($ i === $ len - 1) {
            // son
        }
        //…
        ++ dolarım;
    }

0
foreach ($arquivos as $key => $item) {
   reset($arquivos);
   // FIRST AHEAD
   if ($key === key($arquivos) || $key !== end(array_keys($arquivos)))
       $pdf->cat(null, null, $key);

   // LAST
   if ($key === end(array_keys($arquivos))) {
       $pdf->cat(null, null, $key)
           ->execute();
   }
}

0

Kullanılması reset ($ dizi) ve uç ($ dizi)

<?php

    $arrays = [1,2,3,4,5];

    $first  = reset($arrays);
    $last   = end($arrays);    

    foreach( $arrays as $array )
    {

        if ( $first == $array )
        {
            echo "<li>{$array} first</li>";
        }
        else if ( $last == $array )
        {
            echo "<li>{$array} last</li>";
        }
        else
        {
            echo "<li>{$array}</li>";
        }                

    }

Demo repl.it


-2

Bunu dene:

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);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.