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

meh*_*hdi 466 php foreach loops

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

foreach($array as $element) {
    //code
}
Run Code Online (Sandbox Code Playgroud)

在这个循环中,我想在第一次或最后一次迭代时做出不同的反应.

这该怎么做?

Rok*_*alj 963

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

即将推出的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!';
}
Run Code Online (Sandbox Code Playgroud)

所有PHP版本的解决方案:

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

    end($array);
    if ($key === key($array))
        echo 'LAST ELEMENT!';
}
Run Code Online (Sandbox Code Playgroud)

  • 很棒的答案!比使用一堆数组更清洁. (44认同)
  • 我不认为你应该在foreach中发出reset($ array).来自官方文档(www.php.net/foreach):"由于foreach依赖于内部数组指针,因此在循环内更改它可能会导致意外行为." 并且重置确切地说(www.php.net/reset):"将数组的内部指针设置为其第一个元素" (18认同)
  • 这应该一直冒泡到顶部,因为这是正确的答案.与使用array_shift和array_pop相比,这些函数的另一个优点是数组保持不变,以防稍后需要它们.+1只是为了分享知识. (14认同)
  • 如果您想要保持代码清洁,这绝对是最好的方法.我即将赞成它,但现在我不相信这些数组方法的功能开销是值得的.如果我们只是讨论最后一个元素,那么在循环的每次迭代中都是`end()`+`key()` - 如果它们都是,那么每次调用4个方法.当然,那些将是非常轻量级的操作,可能只是指针查找,但然后文档继续指定`reset()`和`end()`*modify*数组的内部指针 - 所以它比一个更快计数器?可能不是. (13认同)
  • @GonçaloQueirós:它有效.Foreach在数组副本上工作.但是,如果您仍然担心,可以随意在foreach之前移动`reset()`调用,并将结果缓存在`$ first`中. (10认同)
  • 我不明白为什么你们都赞成这一点.只需使用布尔值"first",就好比其他所有东西更好.接受的答案是没有遇到"特殊情况"的方法. (3认同)
  • 如果您担心这种最小化优化,可以在for循环范围之前将key()缓存到变量中.但正如我所说,不要担心. (2认同)

Gum*_*mbo 406

你可以用一个柜台:

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

  • 我不认为downvoting应该在这里发生,因为它也正常工作,并且仍然不像使用`array_shift`和`array_pop`那么垃圾.虽然这是解决方案,但如果我必须实现这样的话,我会坚持使用**Rok Kralj的答案. (23认同)
  • 如果我需要一个计数器,那么我更喜欢使用FOR循环而不是FOREACH. (14认同)
  • 如果你使用`$ i = 1`,你不必担心`$ len - 1`,只需使用`$ len`. (9认同)
  • 第3点是非常错误的,我甚至不知道这是一个HTML问题...... (3认同)
  • @Twan点#3怎么样?或者与这个问题有关,因为它涉及HTML?这是一个PHP问题,很明显......当涉及到标记语义时,它更深层次的事实而不是"一个适当的blahblah总是比blahblah更好(这甚至不是我的意见,这是纯粹的事实)" (2认同)
  • @rkawano 但如果使用 FOR 循环,则无法获得 **named key** (2认同)

小智 116

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

foreach( $items as $item ) {
    if( !next( $items ) ) {
        echo 'Last Item';
    }
}
Run Code Online (Sandbox Code Playgroud)

  • [`next`:**警告**此函数可能返回布尔值**`FALSE`**,但也可能返回非布尔值,其值为**`FALSE`**.有关更多信息,请阅读有关布尔值的部分.使用`===`运算符来测试此函数的返回值.](http://php.net/manual/en/function.next.php) (7认同)
  • 这非常棒,但是要弄清楚其他人指出的问题,它会因诸如[true,true,false,true]之类的数组而总是失败。但是我个人将在处理不包含布尔值“ false”的数组时使用此方法。 (3认同)
  • `next()`应该_NEVER_在foreach循环中使用。它弄乱了内部数组指针。查看文档以获取更多信息。 (3认同)
  • 这个赞成太少了,使用它有什么缺点吗? (2认同)
  • @Kevin Kuyl - 如上面的Pang所提到的,如果数组包含一个PHP评估为false的项(即0,"",null),则此方法将产生意外结果.我修改了代码使用=== (2认同)
  • 因此,我想确定您将(!next($ items))替换为(next($ items)=== FALSE)。我不是php专家,所以只想检查一下。 (2认同)

Hay*_*den 84

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

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

版本2 - 因为除非必要,否则我不厌其烦地使用别人.

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

    if ($index == $len - 1) {
        // last
        // do something
        continue;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这也适用于对象.其他解决方案仅适用于阵列. (8认同)
  • @peteroak @Andrew数组中的元素总数在内部存储为属性,因此不会通过`if($ index == count($ array) - 1)`来执行任何性能命中.见[这里](https://nikic.github.io/2012/03/28/Understanding-PHPs-internal-array-implementation.html). (3认同)
  • @Andrew 这样您就可以为每次迭代不断计算数组的元素。 (2认同)
  • @peteroak是的,实际上它在技术上会损害性能,并且取决于您的计数或循环次数可能很重要。所以无视我的评论:D (2认同)

Car*_*ima 35

您可以从阵列中删除第一个和最后一个元素并单独处理它们.
像这样:

<?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
?>
Run Code Online (Sandbox Code Playgroud)

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

您也可以尽可能避免将HTML与PHP逻辑混合使用.
通过分离这样的事情,您的页面可以更具可读性和可维护性:

<?php
function create_menu($params) {
  //retirive 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
}
?>
Run Code Online (Sandbox Code Playgroud)


Syd*_*ell 18

这很有效!

// 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];
}
Run Code Online (Sandbox Code Playgroud)

感谢@billynoah为您解决最终问题.

  • 最好的事物!我只会澄清`if ($key === $lastkey)`。 (3认同)
  • 这不应该是 `if ($lastkey === $key)` 吗? (2认同)

sst*_*oss 17

尝试找到第一个将是:

$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
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 请编辑您的答案,以添加有关代码如何工作以及如何解决OP问题的说明.很多SO海报都是新手,不会理解您发布的代码. (3认同)
  • 这个答案没有说明如何确定你是否在循环的最后一次迭代中.然而,这是对答案的有效尝试,不应该被标记为不是答案.如果你不喜欢它,你应该投票,而不是标记它. (3认同)

oko*_*man 11

1:为什么不使用简单的for声明?假设您使用的是真实数组,而不是Iterator您可以轻松检查计数器变量是0还是小于整数个元素.在我看来,这是最干净,最容易理解的解决方案......

$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

    }

}
Run Code Online (Sandbox Code Playgroud)

2:您应该考虑使用嵌套集来存储树结构.此外,您可以使用递归函数来改进整个过程.


Iva*_*van 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";

}
Run Code Online (Sandbox Code Playgroud)

  • 只要您可以确定阵列中总有多个元素(如Memochipan所说),它既快速又简单.所以这对我来说不是一个安全的解决方案 - 没有"最佳答案". (4认同)
  • **每个()将从PHP 7.2.0**开始被弃用.另请参阅http://php.net/manual/en/function.each.php (4认同)
  • 如果数组中只有一个元素,则会失败. (2认同)

The*_*per 8

来自@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++;
Run Code Online (Sandbox Code Playgroud)

我还没有对此进行基准测试,但是没有逻辑添加到循环中,这是性能发生的最大打击,所以我怀疑提供高效答案的基准非常接近.

如果你想要实现这种功能,我已经在这里使用了一个这样的iterateList函数.虽然,如果你非常关注效率,你可能想要对gist代码进行基准测试.我不确定所有函数调用引入了多少开销.


小智 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++;
Run Code Online (Sandbox Code Playgroud)

一个小的自制基准显示如下:

test1:模型morg的100000次运行

时间:1869.3430423737毫秒

test2:如果最后一次运行100000次模型

时间:3235.6359958649毫秒

因此很明显,检查费用很高,当然,你添加的检查变量越多,情况就越糟糕;)


Ben*_*ibr 5

使用键和值也可以:

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

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

5er*_*ant 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';
    }
}
Run Code Online (Sandbox Code Playgroud)

然后如何!next( $array )找到最后一个值,如果没有值要迭代$value,它将返回该值。truenext()

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

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