How to determine the last iteration in a foreach loop for multi-dimensional array?

For example, this is the two-dimensional array

$arr = array('foo'=>array('bar'=>'baz','buz'=>'boz'),'boo'=>array('caz'=>'baz','coo'=>'doo','soo'=>'bar'));

Then, I loop it using foreach()…
How to determine if it has got to the end of array ?

I used the code below to determine the last iteration, but it’ll only work when the array value is unique.

$firstLastOffset= count($arr) - 1;
$secondLastOffset= count(end($arr)) - 1;

Then, in the inner foreach loop,

if ($value == $arr[$firstLastOffset][$secondLastOffset]) {
    break 2;
}

But, it only works when the array values is unique… When the array has same values as its last element, it won’t work as expected…
Please tell me how to determine if it’s the last iteration

Can you tell us what exactly you are trying to achieve? Do you need to perform special processing as the last element in the array gets processed, or what is the end goal with trying to determine this?

for ($i=0; $i<count($arr); $++){
    for ($k=0; $k<count($arr[$i]); $k++){
        if ($i == count($arr)-1 && $k == count($arr[$i])-1){
            echo "last element = " . $arr[$i][$k];
        }
    }
}

Yes, of course.
Actually, I’m building the method to build a WHERE clause in sql statement.
This is the piece of code

	public function where(array $expression) {
		if (!isset($this->sql)) {
			throw new BadMethodCallException('You must use SELECT, INSERT, UPDATE, or DELETE first before where clause');
		}
		if (!is_array(reset($expression))) {
			throw new InvalidArgumentException('Invalid format of expression');
		}
		$where = ' WHERE ';
		$lastOffsetExpression = count($expression) - 1;
		$lastOffsetRecord = count(end($expression)) - 1;
		reset($expression);
		foreach ($expression as $operator=> $record) {
			foreach ($record as $column => $value) {
				$where .= $column . ' ' . $operator. ' ' . $value;
				if ($value == $expression[$lastOffsetExpression][$lastOffsetRecord]) {
					break 2;
				}
				$where .= ' AND ';
			}
		}
		$this->sql .= $where;
		return $this;
	}

And, the $expression is an array with keys as the comparison operator (whether it’s = < > <= >= ) and values as an array with the key as column and value as the value.
For example,

$expression = array('=' => array('id'=>'5','name'=>'Amir'), '<' => array('totalPost' => 1000);

But, it only works with unique array

So, it’s only possible to do it with for loop ?
Is it impossible to do it with foreach() ?

So ultimately the problem you are having is there is an extra ’ AND ’ at the end of your SQL statement yes?

If so you can use rtrim to do that.

$sql = "BLAH BLAH BLAH WHERE id = 5 AND name = 'Jared' AND total_posts < 5 AND ";
echo $sql . "\r\n";
$sql = rtrim($sql, ' AND ');
echo $sql . "\r\n";

output:

BLAH BLAH BLAH WHERE id = 5 AND name = 'Jared' AND total_posts < 5 AND 
BLAH BLAH BLAH WHERE id = 5 AND name = 'Jared' AND total_posts < 5

You can do it with foreach using additional counters:

$main_count = count($arr);
$main_index = 0;

foreach ($arr as $key=>$subarr){
    $sub_count = count($subarr);
    $sub_index = 0;
    foreach($subarr as $subkey=>$val){
        if ($main_index == $main_count-1 && $sub_index == $sub_count-1){
            echo "Last element: " . $val;
        }
        $sub_index++;
    }
    $main_index++;        
}
1 Like

Hmm…
I think it’s not efficient, because it adds the “AND” and removes it back.
I want an efficient one…
Anyway, thanks for your solution…

Thanks for your nice solution… :smile:

That depends entirely on your definition of efficient. Your alternative is checking each element if it is the last element in the array. How many checks are you performing against the entire array versus just removing the excess after it is done processing the entire array?

You can use array instead of string for $where and then implode it with AND:

$where = array();
foreach ($expression as $operator=> $record) {
    foreach ($record as $column => $value) {
        $where[] = $column . ' ' . $operator. ' ' . $value;
    }
}
$where = implode(' AND ', $where);

implode() will just glue array elements with provided string (’ AND ')

2 Likes

Now that’s a great suggestion!

1 Like

I would argue that the most effecient way of doing would be not to worry about checking the value for each iteration of the loop and just RTRIM the ‘AND’ but you also have access to CachingIterator / ArrayIterator Classes

$iterator = new CachingIterator(new ArrayIterator($array));

Then in your foreach loop:

if (!$iterator->hasNext()){
//if this is the last item do stuff
}

Very nice suggestion… Thanks a lot!

Nice OOP approach solution. Thanks.
I’d better learn that class first :smile:
But, isn’t it too much? Using object for looping?

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.