Sorting changes the internal order of elements in an array and optionally rewrites the keys to reflect this new order. For example, you might use sorting to arrange a list of scores from biggest to smallest, to alphabetize a list of names, or to order a set of users based on how many messages they posted.
PHP provides three ways to sort arrays—sorting by keys, sorting by values without changing the keys, or sorting by values and then changing the keys. Each kind of sort can be done in ascending order, descending order, or an order defined by a user-defined function.
The functions provided by PHP to sort an array are shown in Table 5-1.
Effect |
Ascending |
Descending |
User-defined order |
---|---|---|---|
Sort array by values, then reassign indexes starting with 0 |
|||
Sort array by values |
|||
Sort array by keys |
The sort( ), rsort( ), and usort( ) functions are designed to work on indexed arrays, because they assign new numeric keys to represent the ordering. They're useful when you need to answer questions like "what are the top 10 scores?" and "who's the third person in alphabetical order?" The other sort functions can be used on indexed arrays, but you'll only be able to access the sorted ordering by using traversal functions such as foreach and next.
To sort names into ascending alphabetical order, you'd use this:
$names = array('cath', 'angela', 'brad', 'dave'); sort($names); // $names is now 'angela', 'brad', 'cath', 'dave'
To get them in reverse alphabetic order, simply call rsort( ) instead of sort( ).
If you have an associative array mapping usernames to minutes of login time, you can use arsort( ) to display a table of the top three, as shown here:
$logins = array('njt' => 415, 'kt' => 492, 'rl' => 652, 'jht' => 441, 'jj' => 441, 'wt' => 402); arsort($logins); $num_printed = 0; echo("<table>\n"); foreach ($logins as $user => $time ) { echo("<tr><td>$user</td><td>$time</td></tr>\n"); if (++$num_printed == 3) { break; // stop after three } } echo("</table>\n"); <table> <tr><td>rl</td><td>652</td></tr> <tr><td>kt</td><td>492</td></tr> <tr><td>jht</td><td>441</td></tr> </table>
If you want that table displayed in ascending order by username, use ksort( ):
ksort($logins); echo("<table>\n"); foreach ($logins as $user => $time) { echo("<tr><td>$user</td><td>$time</td></tr>\n"); } echo("</table>\n"); <table> <tr><td>jht</td><td>441</td></tr> <tr><td>jj</td><td>441</td></tr> <tr><td>kt</td><td>492</td></tr> <tr><td>njt</td><td>415</td></tr> <tr><td>rl</td><td>652</td></tr> <tr><td>wt</td><td>402</td></tr> </table>
User-defined ordering requires that you provide a function that takes two values and returns a value that specifies the order of the two values in the sorted array. The function should return 1 if the first value is greater than the second, -1 if the first value is less than the second, and 0 if the values are the same for the purposes of your custom sort order.
Example 5-3 is a program that lets you try the various sorting functions on the same data.
<?php function user_sort($a, $b) { // smarts is all-important, so sort it first if($b == 'smarts') { return 1; } else if($a == 'smarts') { return -1; } return ($a == $b) ? 0 : (($a < $b) ? -1 : 1); } $values = array('name' => 'Buzz Lightyear', 'email_address' => 'buzz@starcommand.gal', 'age' => 32, 'smarts' => 'some'); if($submitted) { if($sort_type == 'usort' || $sort_type == 'uksort' || $sort_type == 'uasort') { $sort_type($values, 'user_sort'); } else { $sort_type($values); } } ?> <form action="index.php"> <p> <input type="radio" name="sort_type" value="sort" checked="checked" /> Standard sort<br /> <input type="radio" name="sort_type" value="rsort" /> Reverse sort<br /> <input type="radio" name="sort_type" value="usort" /> User-defined sort<br /> <input type="radio" name="sort_type" value="ksort" /> Key sort<br /> <input type="radio" name="sort_type" value="krsort" /> Reverse key sort<br /> <input type="radio" name="sort_type" value="uksort" /> User-defined key sort<br /> <input type="radio" name="sort_type" value="asort" /> Value sort<br /> <input type="radio" name="sort_type" value="arsort" /> Reverse value sort<br /> <input type="radio" name="sort_type" value="uasort" /> User-defined value sort<br /> </p> <p align="center"> <input type="submit" value="Sort" name="submitted" /> </p> <p> Values <?= $submitted ? "sorted by $sort_type" : "unsorted"; ?>: </p> <ul> <?php foreach($values as $key=>$value) { echo "<li><b>$key</b>: $value</li>"; } ?> </ul> </form>
PHP's built-in sort functions correctly sort strings and numbers, but they don't correctly sort strings that contain numbers. For example, if you have the filenames ex10.php, ex5.php, and ex1.php, the normal sort functions will rearrange them in this order: ex1.php, ex10.php, ex5.php. To correctly sort strings that contain numbers, use the natsort( ) and natcasesort( ) functions:
$output = natsort(input); $output = natcasesort(input);
The array_multisort( ) function sorts multiple indexed arrays at once:
array_multisort(array1 [, array2, ... ]);
Pass it a series of arrays and sorting orders (identified by the SORT_ASC or SORT_DESC constants), and it reorders the elements of all the arrays, assigning new indexes. It is similar to a join operation on a relational database.
Imagine that you have a lot of people, and several pieces of data on each person:
$names = array('Tom', 'Dick', 'Harriet', 'Brenda', 'Joe'); $ages = array(25, 35, 29, 35, 35); $zips = array(80522, '02140', 90210, 64141, 80522);
The first element of each array represents a single record—all the information known about Tom. Similarly, the second element constitutes another record—all the information known about Dick. The array_multisort( ) function reorders the elements of the arrays, preserving the records. That is, if Dick ends up first in the $names array after the sort, the rest of Dick's information will be first in the other arrays too. (Note that we needed to quote Dick's zip code to prevent it from being interpreted as an octal constant.)
Here's how to sort the records first ascending by age, then descending by zip code:
array_multisort($ages, SORT_ASC, $zips, SORT_DESC, $names, SORT_ASC);
We need to include $names in the function call to ensure that Dick's name stays with his age and zip code. Printing out the data shows the result of the sort:
echo("<table>\n"); for ($i=0; $i < count($names); $i++) { echo("<tr><td>$ages[$i]</td><td>$zips[$i]</td><td>$names[$i]</td>\n"); } echo("</table>\n"); <table> <tr><td>25</td><td>80522</td><td>Tom</td> <tr><td>29</td><td>90210</td><td>Harriet</td> <tr><td>35</td><td>80522</td><td>Joe</td> <tr><td>35</td><td>64141</td><td>Brenda</td> <tr><td>35</td><td>02140</td><td>Dick</td> </table>
The array_reverse( ) function reverses the internal order of elements in an array:
$reversed = array_reverse(array);
Numeric keys are renumbered starting at 0, while string indexes are unaffected. In general, it's better to use the reverse-order sorting functions instead of sorting and then reversing the order of an array.
The array_flip( ) function returns an array that reverses the order of each original element's key-value pair:
$flipped = array_flip(array);
That is, for each element of the array whose value is a valid key, the element's value becomes its key and the element's key becomes its value. For example, if you have an array mapping usernames to home directories, you can use array_flip( ) to create an array mapping home directories to usernames:
$u2h = array('gnat' => '/home/staff/nathan', 'rasmus' => '/home/elite/rasmus', 'ktatroe' => '/home/staff/kevin'); $h2u = array_flip($u2h); $user = $h2u['/home/staff/kevin']; // $user is now 'ktatroe'
Elements whose original values are neither strings nor integers are left alone in the resulting array. The new array lets you discover the key in the original array given its value, but this technique works effectively only when the original array has unique values.
To traverse the elements in an array in a random order, use the shuffle( ) function. All existing keys, whether string or numeric, are replaced with consecutive integers starting at 0.
Here's how to randomize the order of the days of the week:
$days = array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'); shuffle($days); print_r($days); Array ( [0] => Tuesday [1] => Thursday [2] => Monday [3] => Friday [4] => Wednesday [5] => Saturday [6] => Sunday )
Obviously, the order after your shuffle( ) may not be the same as the sample output here. Unless you are interested in getting multiple random elements from an array, without repeating any specific item, using the rand( ) function to pick an index is more efficient.
Copyright © 2003 O'Reilly & Associates. All rights reserved.