如何确定一个foreach循环中的第一个和最后一个迭代?


494

问题很简单。foreach我的代码中有一个循环:

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

在此循环中,当我们处于第一次或最后一次迭代时,我想做出不同的反应。

这个怎么做?

Answers:


426

您可以使用一个计数器:

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

24
我不认为应该在这里进行投票,因为这也可以正常工作,并且仍然不如使用array_shiftand 那样令人讨厌array_pop。尽管这是我必须实现这种事情时想出的解决方案,但我现在仍然坚持Rok Kralj的回答。
shadyyx13年

15
如果我需要一个计数器,那么我更喜欢使用FOR循环而不是FOREACH。
rkawano

10
如果使用的话$i = 1,您不必担心$len - 1,只需使用$len
aksu 2015年

2
@Twan第三点怎么样?还是涉及到这个问题,因为它涉及HTML?显然,这是一个PHP问题……涉及标记语义时,要归根结底要比“适当的亵渎总是比亵渎更好(这甚至不是我的观点,这纯粹是事实)”要深得多的事实。
德吉2015年

1
@rkawano,但如果使用FOR循环则无法获取命名的密钥
Fahmi

1013

如果您希望不需要在循环外初始化计数器的解决方案,建议将当前的迭代键与告诉您数组的最后一个/第一个键的函数进行比较。

随着即将推出的PHP 7.3,这变得更加有效(并且更具可读性)。

PHP 7.3及更高版本的解决方案:

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

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

适用于所有PHP版本的解决方案:

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

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

44
很棒的答案!比使用一堆数组要干净得多。
保罗J

14
这应该一直冒到顶部,因为这是正确的答案。与使用array_shift和array_pop相比,这些函数的另一个优点是,如果以后需要它们,则可以保留数组的完整性。+1是为了分享知识。
Awemo

13
如果您想保持代码干净,这绝对是最好的方法。我本来打算对它进行投票,但现在我不相信这些数组方法的功能开销是否值得。如果我们只是在谈论最后一个元素,那么循环的每个迭代都为end()+ key()-如果两者都存在,则每次调用4个方法。当然,这些操作非常轻巧,可能只是指针查找,但是文档确实会继续指定它reset()end() 修改数组的内部指针-比计数器快吗?可能不会。
pospi 2012年

19
我不认为您应该在foreach中发出reset($ array)。从官方文档(www.php.net/foreach):“由于foreach依赖内部数组指针,因此在循环内更改它可能导致意外行为。” 和复位精确地完成这项(www.php.net/reset):“设置的阵列,以它的第一个元件的内部指针”
贡萨洛Queirós

11
@GonçaloQueirós:有效。Foreach在数组副本上工作。但是,如果您仍然担心,可以随时将reset()呼叫移至foreach之前,并将结果缓存在中$first
Rok Kralj

121

为了找到最后一项,我发现这段代码每次都有效:

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

2
投票数太少,使用此方法是否有缺点?
凯文·库伊尔


2
@Kevin Kuyl-正如Pang上面提到的,如果数组包含PHP评估为false的项(即0,“”,null),则此方法将产生意外结果。我修改了代码以使用===
Eric Kigathi '16

4
这是非常了不起的,但是为了澄清其他人指出的问题,像这样的数组总是会失败[true,true,false,true]。但是我个人将在处理不包含boolean的数组时使用此方法false
billynoah

4
next()应该永远不要被foreach循环内部使用。它弄乱了内部数组指针。查看文档以获取更多信息。
德拉扎

88

上面的简化版本,假设您没有使用自定义索引...

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

第2版​​-因为除非必要,否则我会讨厌使用else。

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

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

8
这也适用于对象。其他解决方案仅适用于阵列。
拉米2014年

1
这对我来说是最好的答案,但应该简明扼要,不要在foreach循环外声明长度:if($ index == count($ array)-1){...}
安德鲁(Andrew)

2
@Andrew这样,您每次迭代都会不断计数数组的元素。
pcarvalho

1
@peteroak是的,实际上从技术上来讲,它会损害性能,并取决于您的计数或有意义的循环数。所以无视我的评论:D
安德鲁(Andrew)

4
@peteroak @Andrew数组中元素的总数在内部存储为属性,因此这样做不会对性能造成任何影响if ($index == count($array) - 1)。看这里
GreeKatrina

36

您可以从数组中删除第一个和最后一个元素,然后分别处理它们。

像这样:

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

将所有格式删除为CSS而不是内联标签将改善您的代码并加快加载时间。

您还可以尽可能避免将HTML与php逻辑混合。

通过将以下内容分开,可以使您的页面更具可读性和可维护性:

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

尝试找到第一个尝试是:

$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
请编辑您的答案,以添加有关您的代码如何工作以及如何解决OP问题的说明。许多SO张贴者是新手,不会理解您发布的代码。
我惊吓了外星人

4
这个答案没有说明如何确定您是否在循环的最后一次迭代中。但是,这是对答案的有效尝试,不应将其标记为不是答案。如果您不喜欢它,则应对此投反对票,而不是对其进行举报。
ArtOfWarfare 2014年

很明显,在第一次迭代中,他将输入第一个条件,然后将其值更改为false,这样,它将只进入第一次迭代一次。
Mohamed23gharbi 2015年

20

简直是行得通!

// 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];
}

谢谢@billynoah整理完最终问题。


3
最好!我只想澄清一下if ($key === $lastkey)
Krzysztof Przygoda 2015年

2
这不是 if ($lastkey === $key)吗?
Kinetic

1
我得到:PHP Warning: key() expects parameter 1 to be array, integer given in php shell code on line 1
billynoah

1
@Sydwell-阅读错误。 key()正在获取一个整数,而不是end()end()返回最后一个元素的值,并key()期望一个数组作为输入。
billynoah


11

1:为什么不使用简单的for语句?假设您使用的是真实数组而不是真实数组,则Iterator可以轻松地检查计数器变量是否比元素总数少0或1。我认为这是最干净易懂的解决方案...

$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:您应该考虑使用嵌套集来存储树结构。另外,您可以通过使用递归函数来改进整个过程。


如果你打算使用一个for可以循环从1n-1,走ifS中体出来。没有必要反复检查它们。
mpen

9

最佳答案:

$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
当数组中只有一个元素时,此操作将失败。
Memochipan 2014年

4
只要您可以确定数组中总是存在一个以上的元素,那么此操作既快速又容易(如Memochipan所说)。因此,这对我来说不是一个故障保护解决方案-没有“最佳答案”。
Seika85

5
从PHP 7.2.0开始,each()将被弃用。另请参见php.net/manual/en/function.each.php
流程

8

与@morg不同,@morg 的最有效答案foreach仅适用于适当的数组,而不适用于哈希映射对象。该答案避免了循环每次迭代的条件语句的开销,就像在大多数这些答案(包括可接受的答案)中一样,它通过专门处理第一个和最后一个元素并循环遍历中间元素来避免。

array_keys功能可用于使高效的答案像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++;

我还没有进行基准测试,但是循环中没有添加任何逻辑,这是对性能造成最大影响的循环,因此我怀疑提供有效答案的基准测试非常接近。

如果您想对这种功能进行功能化,那么我在这里iterateList函数就已经花了很多时间。但是,如果您过于关注效率,则可能要对基准代码进行基准测试。我不确定所有函数调用都会带来多少开销。


6

对于SQL查询生成脚本,或者对第一个或最后一个元素执行不同操作的任何脚本,它避免使用不必要的变量检查要快得多(几乎快一倍)。

当前接受的解决方案使用一个循环和一个循环内的检查,将其作为every_single_iteration,正确的(快速)方法如下:

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

自制的基准测试结果如下:

test1:运行100000个模型morg

时间:1869.3430423737毫秒

test2:如果最后运行100000个模型

时间:3235.6359958649毫秒

因此,很明显,检查的成本很高,当然,添加的变量检查越多,检查就越糟;)


仅当您可以确定必须具有递增的整数键时,您的代码才有效。$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++;将输出:0: , 1: One, 2: ,
Seika85

将哈希映射强制转换为数组是无法定义的行为,“对象” array()是,{'one':"1 1 1",0:"",1:"One",2:"",3:"",4:"Four"}但空元素将被count忽略,您正在计算已定义的“事物”的数量!赏金牺牲!这个答案值得赏金,但如果是@Morg。走了,这是没有意义的。我会奖励可能不再使用SO的人!如果他回来并改善自己的答案,他应该得到赏金!
mjz19910 2013年

如@ mjz19910所述,哈希映射和数组不可互换。但是,您可以使用array_keys函数获取哈希的属性,可以将其视为数组。请参阅我的“改进”答案
TheMadDeveloper '16

那就是我用来查询的内容:$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

使用键和值也可以:

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

2
这样,您正在比较值,如果数组包含两个相同的元素,则无法使用。
Krzysztof Przygoda

5

即使您想检查a的初次出现,使用Boolean变量仍然是最可靠的方法$value (我发现它在我的情况下和很多情况下都更有用),例如:

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

然后如何!next( $array )找到最后一个如果没有的话$value会返回true的东西next()迭代值值。

而且我更喜欢使用for循环而不是foreach如果要使用计数器,例如:

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

4

当我遇到相同的问题时,我遇到了这个线程。我只需要获取第一个元素,然后重新分析我的代码,直到想到这个。

$firstElement = true;

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

上面的代码很棒而且很完整,但是如果您只需要第一个元素,那么您可以尝试以下代码。


2

不知道是否仍然有必要。但是以下解决方案应与迭代器一起使用,而不是必需的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

您也可以使用匿名函数:

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

还应该提到三件事:

  • 如果未对数组进行严格索引(数字索引),则必须先通过管道传递数组array_values
  • 如果您需要修改,则$element必须通过引用(&$element)将其传递。
  • 从匿名函数外,你需要内部的任何变量,你必须下一个列表他们$indexOfLastElement里面use,如果需要通过参考结构,再一次。

0

您可以使用计数器和数组的长度。

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

    $ i = 0;
    $ len = count($ array);
    foreach($ array as $ item){
        如果($ i === 0){
            //首先
        } else if($ i === $ len-1){
            //最后
        }
        //…
        $ i ++;
    }

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

使用reset($ array)end($ array)

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

    }

演示副本


-2

尝试这个:

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);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.