Multi-Dimensional Array Fun!

8 07 2010

Working with multi-dimensional arrays in PHP can actually be fun. There is virtually every array function built into PHP that you could hope for. That said, one must carefully choose the right one(s).

For the present, let’s consider the case where you have some data in an array that you wish to display in a certain reporting format. Here’s a print_r() view of our data sample:

Array
(
[0] => Array
(
[item] => paper
[unit] => case
[location] => LA
[quantity] => 239
)
[1] => Array
(
[item] => paper
[unit] => case
[location] => OC
[quantity] => 57
)
)

Now the report should look like this:

paper-case-LA-239-OC-57

The goal here is obviously to take the data and get it to display in the above-indicated format. The format of the report suggests that the data in our two arrays comprising the multidimensional array were somehow combined or merged. That sort of thinking could lead one to consider using array_merge(), array_combine() or even array_splice(). If you’re unfamiliar with these functions try them out with any two arrays of your choice to get a sense of what they do that goes beyond their simple sounding names. And, you’ll understand just why they are not appropriate solutions for the present task.

Our data sample is so small that one could easily produce the desired result manually. But, suppose we had 200 or 2000 or more records, then certainly for the sake of efficiency an automated type of reporting would be preferable.

I will explore below several different approaches one might take and discuss the merits of each. Here is our multi-dimensional array that I manually create in PHP but one could also imagine such an array resulting from a database query:


$multi_d = array(
array( "item" => "paper", "unit" => "case", "location" => "LA", "quantity" => 239 ),
array( "item" => "paper", "unit" => "case", "location" => "OC", "quantity" => 57 )
);

We can see that every array or row of data has the same keys and that some of those keys have identical values, such as “item” having a value of “paper” and “unit” representing a label for “case.” We could extract the keys from the first row and use them to extract the values of each row of data. Here’s how to get the keys:

$keys = array_keys( $multi_d[0] );

To fetch the data, we can step through the multi-dimensional array to pick up the values, as follows:

for ($i=0, $max = count($multi_d); $i < $max; $i++)
{
     if ($i == 0) {
	 	echo $multi_d[$i][$keys[0]] . '-';
		echo $multi_d[$i][$keys[1]] . '-';
	 }
	 echo $multi_d[$i][$keys[2]] . '-';
	 echo $multi_d[$i][$keys[3]];
	 if ($i < $max -1) echo '-';
}

The above code achieves the desired result, but is it the best solution? Looking at the for-loop alone, it is difficult to discern what information is being accessed without referring back to the array. What is $multi_d[0][$keys[0]? Is its value that of an item or a unit? While this is one solution that works, let’s look at other alternatives.

Here is an example where we let PHP do the bulk of the work for us:

function test($item,$key )
{
        static $done_item=0;
	static $done_unit=0;
	global $str;

        if ($key == 'item' & !$done_item) {
	     $str .= $item . '-';
		 $done_item++;
	}
	else
	if ($key == 'unit' & !$done_unit) {
	     $str .= $item . '-';
		 $done_unit++;
	}
	else
        if (($key != 'item') && ($key !='unit')) {
	    $str .= $item . '-';
	 }
}

$str = '';
array_walk_recursive($multi_d, 'test');
echo substr($str,0,-1);

The above solution makes use of array_walk_recursive() which is a very handy function for traversing multi-dimensional arrays. It uses the callback test() to examine every key-value pair and display them according to the above format and without any redundancy. We then use substr() to remove the terminating hyphen of $str. The one glaring criticism that this solution is sure to provoke is that $str is a global variable and global variables can result in unexpected side-effects which may produce buggy code. There are other solutions where one can avoid using a global variable.

Knowing a thing or two about array manipulation can come in handy and we’ll use it here, as follows:

$flipped = array();
foreach ($multi_d as $key => $array) {
$flipped[] = array_flip($array);
}
foreach ($flipped as $key => $array) {
foreach($array as $k => $v) {
$temp[] = $k;
}
}
$double_flipped = array_flip(array_flip($temp));
$str = '';
foreach ( $double_flipped as $data) {
$str .= $data . '-';
}
echo substr($str,0,-1);

The above example is as simple as flipping burgers, only in this case less taxing since we’re flipping array key-value pairs. First, we flip the values of all the keys in each array of the multi-dimensional array. We build a new array where each element holds a flipped array. We need all the keys of this new array, so we create another array $flipped in which to store them. We step through each element of $flipped and thereby each array, finding the key of each key-value pair and assigning it to the new array $temp which looks like this:


array
0 => string 'paper' (length=5)
1 => string 'case' (length=4)
2 => string 'LA' (length=2)
3 => int 239
4 => string 'paper' (length=5)
5 => string 'case' (length=4)
6 => string 'OC' (length=2)
7 => int 57

We flip the array again which will have the effect of wiping out the duplicate keys. The extra array_flip is necessary so that we can restore the keys as the original values and iterate through them in the final foreach loop, concatenating each with a hyphen and displaying the result by removing the pesky terminating hyphen with substr().

While the code sample works in this particular case, it is an inadvisable way to safely handle data. Suppose LA had a quantity of 100 and so did OC. Using array_flip() at a certain point would assuredly result in the loss of data. So, let’s continue our exploration of other options.

$str = join('-',array_slice($multi_d[0],0,2)) . '-';
for ($i=0, $max=count($multi_d); $i < $max; $i++) {
	 	$str .= $multi_d[$i]['location'] . '-';
		$str .= $multi_d[$i]['quantity'] . '-';
}
echo substr($str,0,-1);

This solution does what we need and safely handles the data. It hinges on us knowing where the duplicate key-value pairs are and we extract them easily with array_slice(), joining each value with a hyphen as we build our a string to display. The for-loop allows us to focus on the data that we want as we go through each row or rather array of $multi_d and extract the location and quantity information. Lastly, we use substr() to neatly display the data minus the string’s terminating hyphen.

But, suppose you didn’t know where the duplicate key-value pairs were located. Then what? You could alter the code slightly, as follows:

$str = join('-',(array_intersect_assoc($multi_d[0],$multi_d[1]) )) . '-';
for ($i=0, $max=count($d); $i < $max; $i++) {
	 	$str .= $multi_d[$i]['location'] . '-';
		$str .= $multi_d[$i]['quantity'] . '-';
}
echo substr($str,0,-1);

In this latest example, we use array_intersect_assoc() which picks out the common elements in 2 or more arrays, joining the values with hyphens and adding a final hyphen and assign the resulting string to $str. We build the string up just as we did the previous example and can expect the same output.

Other options for tackling this problem would involve using SPL (the standard PHP library), but I’ll leave that for another day.

This work is licensed under a Creative Commons License
.

Advertisements

Actions

Information

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s




%d bloggers like this: