253

I am writing a SQL query creator using some parameters. In Java, it's very easy to detect the last element of an array from inside the for loop by just checking the current array position with the array length.

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

In PHP they have non-integer indexes to access arrays. So you must iterate over an array using a foreach loop. This becomes problematic when you need to take some decision (in my case to append or/and parameter while building query).

I am sure there must be some standard way of doing this.

How do you solve this in PHP?

Thamilhan
  • 13,040
  • 5
  • 37
  • 59
Vaibhav Kamble
  • 3,579
  • 5
  • 23
  • 16
  • 2
    Are you trying to determine if you should concat an "AND" or "OR" between parts of a where clause? – Darryl Hein Mar 20 '09 at 07:32
  • 1
    just pointing out that you should store the total in a variable instead of calling a method for every iteration. for(int i=0, int t = arr.length; i – OIS Mar 20 '09 at 12:08
  • http://stackoverflow.com/questions/156650/does-the-last-element-in-a-loop-deserve-a-separate-treatment – xtofl Dec 14 '09 at 14:55
  • Take a look at this solution: http://stackoverflow.com/a/29474468/1478566 – vbarbarosh Apr 06 '15 at 15:41
  • use end(arrray) [use simmilar answers](http://stackoverflow.com/questions/9700012/php-foreach-if-array-last) – Visakh B Sujathan Dec 16 '15 at 08:51
  • What are you trying to do with it? Are you going to be already iterating over it anyways? There's end() which will move the array pointer to the end of the array or you can use array_pop() to iterate through it as well. – Hayden Mar 01 '17 at 16:48

35 Answers35

362

It sounds like you want something like this:

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

That being said, you don't -have- to iterate over an "array" using foreach in php.

hakre
  • 193,403
  • 52
  • 435
  • 836
Richard Levasseur
  • 14,562
  • 6
  • 50
  • 63
  • I think I will go for this solution as it is almost similar to the code I posted. Even Jeremy's answer is well fit but I think it got little complex compared to this one. I have not ran any tests but I guess this answer shall be faster as it is not extracting array of keys.This shall have O(1) speed – Vaibhav Kamble Mar 20 '09 at 07:09
  • 24
    `$numItems = count($arr)` trick is not needed and reduces readability - in PHP there is no performance penalty for accessing count($arr) each time. The reason is that items count is internally saved as special field in the array header and is **not** calculated on-the-fly. This trick comes from other languages (C, Java?,...). – johndodo Dec 22 '11 at 11:57
  • 7
    That's interesting @johndodo that there's no performance penalty for accessing count($arr) each time. Do you have any links/sources to where this particular optimisation is documented? Thanks! – zuallauz Jan 31 '12 at 03:19
  • 2
    It's rather sad that in PHP the most correct solution to this problem looks rather inelegant :( – kizzx2 Jan 20 '13 at 16:55
  • Another up vote to this answer. I tried this method and the other method described below, using `end(...)`. This method proved to be faster with very large loops (~100,000 elements) with string keys. The other is more elegant, but unfortunately, it is slower. – javsmo Jan 08 '14 at 19:00
  • What does ++$i means ? Is it like "the value of $i++" into a if statement ? – tomsihap Mar 01 '16 at 17:16
  • @tomsihap it's very similar to `$i++` in that it increments the value stored in that variable by one. The difference is the return value: `++$i` returns the value BEFORE the increment `$i++` returns it AFTER. So, in the example, the very first iteration will see the conditional checking `if (0 === $numItems)` – Bobby Jack Mar 01 '18 at 10:29
  • @BobbyJack thanks for the answer. Why not simply call it "$i" then ? Since $i++ adds 1 to $i as its current value in the loop – tomsihap Mar 01 '18 at 10:52
  • 1
    @tomsihap $i needs to be incremented inside the loop, in tandem with the array iteration. $i needs to represent the number of the item in the array, so that it can be used to determine when the last item has been reached. Without the ++ the loop would only ever compare "0" with the total number of items. – Bobby Jack Mar 02 '18 at 14:10
  • 1
    SINCE PHP 7.3 UNTIL NOW You could get the value of the last key of the array using `array_key_last($array) ` – TechDogLover OR kiaNasirzadeh Oct 11 '20 at 17:28
229

You could get the value of the last key of the array using end(array_keys($array)) and compare it to the current key:

$last_key = end(array_keys($array));
foreach ($array as $key => $value) {
    if ($key == $last_key) {
        // last element
    } else {
        // not last element
    }
}
Paige Ruten
  • 172,675
  • 36
  • 177
  • 197
  • 2
    +1 I agree - the other solutions rely on the array having numeric indexes. – Patrick Glandien Mar 20 '09 at 06:07
  • 21
    In my own defense, my answer doesn't rely on the array having numeric keys :) – Richard Levasseur Mar 20 '09 at 06:53
  • 2
    string comparison is slower then integers, and not always accurate when comparing strings to integers (you should at least have used ===). Im voting this down. – OIS Apr 01 '09 at 11:10
  • 4
    it's elegant, but causes STRICT NOTICE because "end" expects a reference value :( – Wiliam Sep 11 '12 at 18:37
  • 13
    Fix for STRICT NOTICE: `$lastKey = array_search(end($array), $array);` – Ajax Feb 24 '13 at 10:24
  • TURN OFF STRICT MODE: `error_reporting(E_ALL ^ E_STRICT);` , turn all error messages and strict mode on - `error_reporting(-1);` – jave.web Sep 03 '13 at 12:06
  • 2
    This should be the fastest way to get the last key of an array without triggering error messages: end($array); $last_key = key($array); – OIS Feb 26 '15 at 15:44
  • 2
    This doesnt work because you cannot use the result of array_keys with end(). See: https://stackoverflow.com/questions/4636166/only-variables-should-be-passed-by-reference. First you have to assign the result of array_keys: $keys = array_keys($array); Then you can get the last entry: $lastKey = end($keys); – omarjebari Jul 25 '17 at 10:44
55

Note: This doesn't work because calling next() advances the array pointer, so you're skipping every other element in the loop


why so complicated?

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

This will add a , behind every value except the last one!

PiTheNumber
  • 22,828
  • 17
  • 107
  • 180
Trikks
  • 735
  • 5
  • 2
  • 3
    Not if the next $input contains a boolean value of false, which is a major problem with next(). – soulseekah Apr 07 '11 at 15:02
  • 38
    Unless I'm mistaken, this doesn't work because calling next() advances the array pointer, so you're skipping every other element in the loop. – Jordan Lev Dec 23 '11 at 07:47
  • 2
    Doesn't seem to work for me. The second last element doesn't get the comma but it should. – zuallauz May 26 '12 at 02:17
  • 1
    If the value equates to bool false it doesn't work. Also doesn't print the last comma between the second to last and last value. – OIS Feb 26 '15 at 16:16
  • 2
    A note for anyone wanting to use this in PHP7 - the array pointer doesn't move in foreach loops, and this will not work. – Scott Flack Oct 30 '17 at 05:52
  • Update for PHP 7 you can simplify this easily with `echo next($input) ? "," : NULL;` – Tyler Lazenby Apr 24 '19 at 22:33
  • @ScottFlack it worked for me. The `next()` call does move the internal array pointer so it doesn't matter that `foreach` doesn't. – Cave Johnson Jun 02 '23 at 21:14
28

When toEnd reaches 0 it means it is at the last iteration of the loop.

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

The last value is still available after the loop, so if you just want to use it for more stuff after the loop this is better:

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

If you do not want to treat the last value as special inside loops. This should be faster if you have large arrays. (If you reuse the array after the loop inside the same scope you have to "copy" the array first).

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

And to answer your original question "in my case to append or/and parameter while building query"; this will loop over all the values, then join them together to a string with " and " between them but not before the first value or after the last value:

$params = [];
foreach ($array as $value) {
    $params[] = doSomething($value);
}
$parameters = implode(" and ", $params);
OIS
  • 9,833
  • 3
  • 32
  • 41
  • 2
    Of course it will perform the --$toEnd for every iteration, thats the point. If I moved it outside the loop, it would not work anymore. – OIS Apr 01 '09 at 11:02
  • The simplest method ever used. `$lastValue = array_pop($array);` Thank you. – Elias Nicolas Sep 20 '15 at 06:21
25

One way could be to detect if the iterator has next. If there is no next attached to the iterator it means you are in the last loop.

foreach ($some_array as $element) {
    if(!next($some_array)) {
         // This is the last $element
    }
}
Raheel
  • 8,716
  • 9
  • 60
  • 102
  • @DamienDebin Did you test it in PHP7? It worked for me. I don't see why it matters that `foreach` does not use the internal array pointer. `next()` _does_ use and advance the internal array pointer and will be false for the last element check. The only problem I see is that falsey values will trigger the last element check. – Cave Johnson Jun 02 '23 at 21:11
25

SINCE PHP 7.3 :

You could get the value of the last key of the array using array_key_last($array) and compare it to the current key:

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

There are already many answers, but it's worth to look into iterators as well, especially as it has been asked for a standard way:

$arr = range(1, 3);

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

You might find something that does work more flexible for other cases, too.

hakre
  • 193,403
  • 52
  • 435
  • 836
  • Great answer. I appreciate that you are using the features of the language that are intended for the task. `i=0;` and `++i;` have always seemed hackish in a scripting language like PHP. – CheddarMonkey Oct 05 '18 at 23:22
15

to get first and last element from foreach array

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

    if ($value === end($array)) {
        echo 'LAST ITEM!';
    }
}
Darkcoder
  • 802
  • 1
  • 9
  • 17
  • 1
    this seams to be very slow since you allways call 2 function to get compare value – Dwza Nov 30 '20 at 16:25
8

So, if your array has unique array values, then determining last iteration is trivial:

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

As you see, this works if last element is appearing just once in array, otherwise you get a false alarm. In it is not, you have to compare the keys (which are unique for sure).

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

Also note the strict coparision operator, which is quite important in this case.

Rok Kralj
  • 46,826
  • 10
  • 71
  • 80
8

Don't add a comma after the last value:

The array:

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

The function:

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

The result:

print $result;

lorem, ipsum, dolor, sit, amet

Santo Boldizar
  • 1,255
  • 14
  • 17
6

You can still use that method with associative arrays:

$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
  • 537,072
  • 198
  • 649
  • 721
5

Assuming you have the array stored in a variable...

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

This should be the easy way to find the last element:

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

Reference : Link

Dulitha K
  • 2,088
  • 1
  • 19
  • 18
4

If you need to do something for every element except either the first or the last and only if there is more than one element in the array, I prefer the following solution.

I know there are many solutions above and posted months/one year before mine, but this is something I feel is fairly elegant in its own right. The check every loop is also a boolean check as opposed to a numeric "i=(count-1)" check, which may allow for less overhead.

The structure of the loop may feel awkward, but you can compare it to the ordering of thead (beginning), tfoot (end), tbody (current) in HTML table tags.

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

For instance, in web development terms, if you want to add a border-bottom to every element except the last in an unordered list (ul), then you can instead add a border-top to every element except the first (the CSS :first-child, supported by IE7+ and Firefox/Webkit supports this logic, whereas :last-child is not supported by IE7).

You can feel free to reuse the $first variable for each and every nested loop as well and things will work just fine since every loop makes $first false during the first process of the first iteration (so breaks/exceptions won't cause issues).

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

Example 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)
Ankit Aggarwal
  • 1,546
  • 10
  • 11
  • Thanx, this is my favourite solution! It's very flexible and costs only a boolean. BTW, i think this will work for array containing *at least* one element too (not only *more than* one element). – j.c Oct 05 '16 at 15:08
3

I have a strong feeling that at the root of this "XY problem" the OP wanted just implode() function.

Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
  • 1
    true. There are cases though where implode is just not as practical. Imagine for example trying to implode a long string of html with lots of dynamic variables in it. Sure, you could do a ob_start/ob_get_clean on it, or just build it as a $str ='...'. But, there are times when this could be considered just a tad overkill – Alastair Brayne Jul 16 '13 at 17:30
3

As your intention of finding the EOF array is just for the glue. Get introduced to the below tactic. You need not require the 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
  • 8,944
  • 10
  • 43
  • 53
2

How about using "end"? http://php.net/manual/en/function.end.php

Çağ
  • 21
  • 1
2
$array  = array("dog", "rabbit", "horse", "rat", "cat");
foreach($array as $index => $animal) {
    if ($index === array_key_first($array))
        echo $animal; // output: dog

    if ($index === array_key_last($array))
        echo $animal; // output: cat
}
FsCode
  • 112
  • 8
2

It sounds like you want something like this:

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

foreach($array as $key => $value)
{
    if(end($array) === $value)
    {
       echo "last index!" . $value;
    }
}
Ashique CM
  • 29
  • 2
  • 2
    Using the value usually isn't a good idea because it won't work properly if the array has two identical values. – orrd Jan 07 '14 at 20:23
1

I kinda like the following as I feel it is fairly neat. Let's assume we're creating a string with separators between all the elements: e.g. a,b,c

$first = true;
foreach ( $items as $item ) {
    $str = ($first)?$first=false:", ".$item;
}
Alastair Brayne
  • 731
  • 1
  • 9
  • 22
1

Here's my solution: Simply get the count of your array, minus 1 (since they start in 0).

$lastkey = count($array) - 1;
foreach($array as $k=>$a){
    if($k==$lastkey){
        /*do something*/
    }
}
ITWitch
  • 1,729
  • 5
  • 20
  • 38
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>";

}

Reference

Ayman Elshehawy
  • 2,746
  • 23
  • 21
1

you can do a count().

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

or if you're looking for ONLY the last element, you can use end().

end(arr);

returns only the last element.

and, as it turns out, you CAN index php arrays by integers. It's perfectly happy with

arr[1];
helloandre
  • 10,541
  • 8
  • 47
  • 64
  • 1
    The drawback in end(arr) is it sets the array's internal pointer to the last element.. – Vijay Sep 29 '11 at 14:14
  • No, you **SHOULDN'T** use integers to access the arrays unless you know that the keys are numeric and sequential. Consider: `$a = array(0=>'A', 2=>'B', 'aaa'=>'C')`. What do you get if you access `$a[count($a)-1]`? – johndodo Dec 22 '11 at 12:02
1

If it is a single dimensional array you can do this to keep it short and sweet:

foreach($items as $idx => $item) {
    if (!isset($items[$idx+1])) {
        print "I am last";
    }
}
Justin Vincent
  • 1,329
  • 2
  • 14
  • 22
1

You could also do something like this:

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

     // more code
}
KOGI
  • 3,959
  • 2
  • 24
  • 36
  • end returns the value not the array, so the way you made it doesnt work. string comparison is also slower then integer. – OIS Apr 01 '09 at 11:06
  • You are right. it should be end($elements); $endKey = key($elements); – KOGI Apr 01 '09 at 22:13
0

I personally use this kind of construction which enable an easy use with html < ul > and < li > elements : simply change the equality for an other property...

The array cannot contains false items but all the others items which are cast into the false boolean.

$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 Belgium
  • 2,330
  • 4
  • 30
  • 46
0

You can dirctly get last index by:

$numItems = count($arr);

echo $arr[$numItems-1];

Denis Omeri
  • 610
  • 1
  • 9
  • 21
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
  • 513
  • 1
  • 6
  • 10
0

Here's another way you could do it:

$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
  • 13,044
  • 11
  • 55
  • 76
  • This answer is missing its educational explanation. echo sprintf() is an "antipattern". There is absolutely no reason that anyone should ever write echo sprintf() in any code for any reason -- it should be printf() every time. – mickmackusa Apr 08 '22 at 02:29
0

If I understand you, then all you need is to reverse the array and get the last element by a pop command:

   $rev_array = array_reverse($array);

   echo array_pop($rev_array);
James
  • 290
  • 3
  • 8
0

You could also try this to make your query... shown here with 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;
?>
Mark
  • 53
  • 6
0

Try this simple solution

$test = ['a' => 1, 'b' => 2, 'c' => 3];

$last_array_value = end($test);

foreach ($test as $key => $value) {
   if ($value === $last_array_value) {
      echo $value; // display the last value  
   } else {
     echo $value; // display the values that are not last elements 
   }
}
larp
  • 1,017
  • 1
  • 14
  • 23
0

More easy by end() function is an inbuilt function in PHP and is used to find the last element of the given array. The end() function changes the internal pointer of an array to point to the last element and returns the value of the last element.

Below is an example with non integer index:

<?php
    $arr = array(
            'first' => 
                array('id' => 1, 'label' => 'one'), 
            'second' => 
                array('id' => 2, 'label' => 'two'), 
            'last' => 
                array('id' => 9, 'label' => 'nine')
        );
    $lastIndexArr = end($arr);
    print_r($lastIndexArr);

Check here the last array as an output.

lazyCoder
  • 2,544
  • 3
  • 22
  • 41
0

For SQL query generating scripts, or anything that does a different action for the first or last elements, it is much faster (almost twice as fast) to avoid using unneccessary variable checks.

The current accepted solution uses a loop and a check within the loop that will be made every_single_iteration, the correct (fast) way to do this is the following :

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

A little homemade benchmark showed the following:

test1: 100000 runs of model morg

time: 1869.3430423737 milliseconds

test2: 100000 runs of model if last

time: 3235.6359958649 milliseconds

Morg.
  • 89
  • 1
  • 1
0

Another way to go is to remember the previous loop cycle result and use that as the end result:

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