PHP Array Iteration

Iteration is probably one of the most common operations you will perform with arrays— besides creating them, of course. Unlike what happens in other languages, where arrays are all enumerative and contiguous, PHP’s arrays require a set of functionality that matches their flexibility, because “normal” looping structures cannot cope with the fact that array keys do not need to be continuous—or, for that matter, enumerative. Consider, for example, this simple array:

<?php
$a = array('a' => 10, 10 => 20, 'c' => 30);
?>

It is clear that none of the looping structures we have examined so far will allow you to cycle  through the elements of the array—unless, that is, you happen to know exactly what its keys are, which is, at best, a severe limitation on your ability to manipulate a generic array.

The Array Pointer
Each array has a pointer that indicates the “current” element of an array in an iteration. The pointer is used by a number of different constructs, but can only be manipulated through a set of functions and does not affect your ability to access individual elements of an array, nor is it affected by most “normal” array operations.

The pointer is, in fact, a handyway ofmaintaining the iterative state of an array without needing an external variable to do the job for us. The most direct way of manipulating the pointer of an array is by using a series of functions designed specifically for this purpose. Upon starting an iteration over an array, the first step is usually to reset the pointer to its initial position using the
reset() function; after that, we can move forward or backwards by one position by using prev() and next() respectively. At any given point, we can access the value of the current element using current() and its key using key(). Here’s an example:

<?php
$array = array('foo' => 'bar', 'baz', 'bat' => 2);

function displayArray($array){
   reset($array);
   while(key($array) !== null){
      echo key($array) .": " .current($array) . PHP_EOL;
      next($array);
   }
}
?>

Here, we have created a function that will display all the values in an array. First, we call reset() to rewind the internal array pointer. Next, using a while loop, we display the current key and value, using the key() and current() functions. Finally, we advance the array pointer, using next(). The loop continues until we no longerhave a valid key.

Since you can iterate back-and-forth within an array by using its pointer, you could—in theory—start your iteration from the last element (using the end() function to reset the pointer to the bottom of the array) and then making your way to back the beginning:

<?php
$a = array(1, 2, 3);
end($a);
while(key($array) !== null){
   echo key($array) .": " .current($array) . PHP_EOL;
   prev($array);
}
?>

Note how, in the last two example, we check whether the iteration should continue by comparing the result of a call to key() on the array to NULL. This only works because we are using a non-identity operator—using the inequality operator could cause some significant issues if one of the array’s elements has a key that evaluates to integer zero.

An EasierWay to Iterate
As you can see, using this set of functions requires quite a bit of work; to be fair, there are some situations where they offer the only reasonable way of iterating through an array, particularly if you need to skip back-and-forth between its elements.

If, however, all you need to do is iterate through the entire array fromstart to finish, PHP provides a handy shortcut in the formof the foreach() construct:

<?php
$array = array('foo', 'bar', 'baz');
foreach($array as $key => $value) {
   echo "$key: $value";
}
?>

The process that takes place here is rather simple, but has a few important gotchas. First of all, foreach operates on a copy of the array itself; this means that changes made to the array inside the loop are not reflected in the iteration—for example, removing an item from the array after the loop has begun will not cause foreach to skip over that element. The array pointer is also always reset to the beginning of the array prior to the beginning to the loop, so that you cannot manipulate it in such a way to cause foreach to start from a position other than the first element of the array.

PHP 5 also introduces the possibility of modifying the contents of the array directly by assigning the value of each element to the iterated variable by reference rather than by value:

<?php
$a = array(1, 2, 3);
foreach($a as $k => &$v){
   $v += 1;
}
var_dump($a); // $a will contain (2, 3, 4)
?>

While this technique can be useful, it is so fraught with peril as to be something best left alone.

Consider this code, for example:
<?php
$a = array ('zero','one','two');
foreach($a as &$v) {
}
foreach($a as $v) {
}
print_r($a);
?>

It would be natural to think that, since this little script does nothing to the array, it will not affect its contents… but that’s not the case! In fact, the script provides the following output:

Array
(
[0] => zero
[1] => one
[2] => one
)

As you can see, the array has been changed, and the last key now contains the value ’one’. How is that possible? Unfortunately, there is a perfectly logical explanation— and this is not a bug. Here’s what going on. The first foreach loop does not make any change to the array, just as we would expect. However, it does cause $v to be assigned a reference to each of $a’s elements, so that, by the time the loop is over,$v is, in fact, a reference to $a[2].

As soon as the second loop starts, $v is now assigned the value of each element. However, $v is already a reference to $a[2]; therefore, any value assigned to it will be copied automatically into the last element of the arrays! Thus, during the first iteration, $a[2] will become zero, then one, and then one again, being effectively copied on to itself. To solve this problem, you should always unset the variables you use in your by-reference foreach loops—or, better yet, avoid using the  former altogether.

Passive Iteration
The array_walk() function and its recursive cousin array_walk_recursive() can be used to perform an iteration of an array in which a user-defined function is called. Here’s an example:

<?php
function setCase(&$value, &$key){
   $value = strtoupper($value);
}

$type = array('internal', 'custom');
$output_formats[] = array('rss', 'html', 'xml');
$output_formats[] = array('csv', 'json');
$map = array_combine($type, $output_formats);
array_walk_recursive($map, 'setCase');
var_dump($map);
?>

Using the custom setCase() function, a simplewrapper for strtoupper(), we are able to convert each each of the array’s values to uppercase. One thing to note about array_walk_recursive() is that it will not call the user-defined function on anything but scalar values; because of this, the first set of keys, internal and custom, are never passed in.

The resulting array looks like this:
array(2) {
["internal"]=>
&array(3) {
[0]=>
string(3) "RSS"
[1]=>
string(4) "HTML"
[2]=>
string(3) "XML"
}
["custom"]=>
&array(2) {
[0]=>
string(3) "CSV"
[1]=>
string(4) "JSON"
}
}

Next Topic | Previous Topic

Post to Twitter Post to Digg Post to Facebook Post to Google Buzz Send Gmail

Leave a Comment

Your email address will not be published. Required fields are marked *