Jan 23, 2012

f Comment

PHP Sorting Functions Are Not Working! Why?

Amazon PHP provides a set of sorting functions that allows you to sort by keys or sort by values or sort by comparison functions that you define. I am trying to sort part of a multi dimensional array but it's not working. Here's the array in question:
$array = array();
$array['brand']['AE']['shirts']['orange shirt']=1;
$array['brand']['AE']['shirts']['black shirt']=1;
$array['brand']['Banana Republic']['shirts']['white shirt']=1;
$array['brand']['Calvin Klein']['shirts']['blue shirt']=1;
$array['brand']['Calvin Klein']['shirts']['red shirt']=1;
$array['brand']['Calvin Klein']['shirts']['grey shirt']=1;
I'd like to sort the array so that the brand that has the MOST shirts is the first entry I'd access while traversing the array via foreach(). This means had my sorting worked the array would've been the same as the elements being created in this order:
$array = array();
$array['brand']['Calvin Klein']['shirts']['blue shirt']=1;
$array['brand']['Calvin Klein']['shirts']['red shirt']=1;
$array['brand']['Calvin Klein']['shirts']['grey shirt']=1;
$array['brand']['AE']['shirts']['orange shirt']=1;
$array['brand']['AE']['shirts']['black shirt']=1;
$array['brand']['Banana Republic']['shirts']['white shirt']=1;
How do I sort this multi dimensional array in PHP? What are the potential traps I may fall victim to?

How PHP Array Order Works
PHP array uses insertion order as the default ordering mechanism. This means whatever inserted first will be accessed first when you traverse the array; whatever inserted second will be accessed second; so on and so forth. This means whatever order you want to achieve beyond the natural ordering you'll need to make it happen yourself. Don't despair because PHP provides built in sorting functions. We'll go through them next.

Introduction of Sorting Functions in PHP
As mentioned earlier PHP provides an array of sorting functions (no pun intended) including sort(), asort(), ksort(), rsort(), uasort(), uksort(), usort(). They may seem many but it's easy to know what each does.

'k' means sort by keys. 'r' means sort in reverse order, also known as descending order. 'u' means sort using a user defined comparison function. 'a' means sort by values and the values keep their original keys. Without 'a' in the sorting function's name the function may assign new keys to the elements in the resulting array. For example sort() sorts an array by the values but assigned new keys, starting from 0, to the values in the resulting array.
Refer to PHP's official online manual for more details of how to use each function.

Solution
To solve our problem earlier, we need to define a comparison function for sorting. Here it is:
// Comparison function
function cmp($a, $b) {
 $ac=count($a['shirts']);
 $bc=count($b['shirts']);
    if ($ac == $bc) {
        return 0;
    }
    return ($ac < $bc) ? 1 : -1;
}
As you can see this comparison function simply compares the number of shirts of each sub array and puts the brand with the most shirts as the first element in the original array. Now simply call the following to sort the values of this array with this comparison function:

uasort($array['brand'], "cmp");
We are done. Read further to discover a BIG trap you may fall into which will make you scream "PHP sorting is NOT working!"

A Big Trap
Let's suppose you have another top level dimension called 'color' which is at the same level as 'brand'. Just as 'brand' sub array we want to access first the color with MOST, say, t-shirts. This means you'll probably use the following foreach() loop to sort the original array:
foreach($array as $egBrand => $brandArr){
    uasort($brandArr, "cmp");
}
Now you find out the $array is NOT sorted at all! Why? What's happening?

It turns out that in the foreach() $egBrand and $brandArr are copied by VALUE, not by REFERENCE!
When you sort $brandArr via usort() you are just sorting the copy and NOT the original array! To fix the issue here are two methods:

Method #1
foreach($array as $egBrand => $brandArr){
    uasort($array[$egBrand], "cmp");
}

Method #2
foreach($array as $egBrand => $brandArr){
    uasort($brandArr, "cmp");
    $array[$egBrand]=$brandArr;
}
Method #1 is preferred as uasort() sorts the original array directly. Method #2 simply sorts the copied array and assign the result back to the original array.

Now you should understand how PHP sorting works and how to avoid a potential trap. Great job!

Questions? Let me know!
Please leave a comment here!
One Minute Information - by Michael Wen
ADVERTISING WITH US - Direct your advertising requests to Michael