Community Pick: Many members of our community have endorsed this article.

Advanced PHP: Custom Sorting and Multidimensional Sorting

gr8gonzoConsultant
CERTIFIED EXPERT
Published:
Updated:
Everyone has probably been asked, "Which (food / movie / dress / whatever) do you like better, A or B?" Really, there's only three ways to answer this type of question: "I like A better" or "I like B better" or "I like them the same."

This is ALL you need to know in order to understand how to use the usort() function in PHP. The usort() function lets you sort an array using a function that you've created to compare two things. All PHP asks the function to do is tell it which it likes better: $a or $b.

You don't have to worry about the array itself - all you do is create a function that takes in two parameters $a and $b. Then your function does whatever it needs to do in order to determine whether $a is greater than $b. If $a is greater than $b, the function should return a 1. If $b is greater, then the function should return a -1. If $a and $b are the same, then the function returns a 0.

Someone wanted to be able to sort an array of strings by the first character after the dash in the string. In other words, their array looked like:

$myArray = array(
  "foo1-Anthony",
  "foo2-Xavier",
  "foo3-Henry",
  "foo4-Jarrod"
);

...and they wanted to sort by the name. Obviously, none of the built-in sorting functions will do, so usort lets us do our own thing. In this case, I wrote a function to split the strings given by the dash, so I could do a simple comparison on the strings the person wanted to test:
function sortAfterTheDash($a,$b)
{
  list($whateverA,$testA) = explode("-",$a);
  list($whateverB,$testB) = explode("-",$b);
  
  if($testA > $testB) { return 1; }
  if($testA < $testB) { return -1; }
  if($testA == $testB) { return 0; }
}

To call it, we simply run:

usort($myArray, "sortAfterTheDash");

The result is that $myArray gets sorted the way we want it, using the strings after the dash! Basically what usort() does is go through your array, two elements at a time, then passes those two elements to your function. If you were to do the same thing manually, you would do it like this:

$newArray = array();
$result = sortAfterTheDash("foo1-Anthony","foo2-Xavier");
// Based on the result, insert Anthony into the array first, and Xavier second.
// $newArray = array("foo1-Anthony","foo2-Xavier");

$result = sortAfterTheDash("foo2-Xavier","foo3-Henry");
// Based on the result, insert Henry into the array BEFORE Xavier
// $newArray = array("foo1-Anthony","foo3-Henry","foo2-Xavier");

$result1 = sortAfterTheDash("foo3-Henry","foo4-Jarrod");
$result2 = sortAfterTheDash("foo4-Jarro","foo2-Xavier");
// Based on the results, insert Jarrod into the array BETWEEN Henry and Xavier
// $newArray = array("foo1-Anthony","foo3-Henry","foo4-Jarrod","foo2-Xavier");
Obviously it's far simpler to let PHP handle all that stuff for you!

Now let's move onto a more advanced topic - sorting a multi-dimensional array using array_multisort(). A LOT of people have no idea how to use this very powerful function, but it's usually because it's one of the few functions that requires a fair amount of "preparation" to use. Fear not - I have a shortcut that I will share with you, but let's understand array_multisort() first. Let's say you have an array that looks like this:

$people = array(
  "Joe" => array("Last Name" => "Schmoe", "Income" => 20000),
  "Bill" => array("Last Name" => "Gaites", "Income" => 200000000),
  "William" => array("Last Name" => "Gaites", "Income" => 200),
  "Mary" => array("Last Name" => "Bartley", "Income" => 100000),
);

Now let's say you want to sort by ascending "Income". Unfortunately, array_multisort doesn't just take a field name and sort on it. What you have to do is first extract the values you want to sort by to ANOTHER array. So in this case, you would want to loop through $people and create a NEW array called $incomes:

$incomes = array();
foreach($people as $person)
{
  $incomes[] = $person["Income"];
}

Then you would call array_multisort like this:
array_multisort($incomes,SORT_ASC,$people);

What happens is that array_multisort FIRST sorts the $incomes array in ascending order (SORT_ASC) and keeps track of which record moved to which spot (e.g. record 4, which was 100000, is now record 3 after being sorted). It creates a map like this:

Index 0 is now at Index 1
Index 1 is now at Index 3
Index 2 is now at Index 0
Index 3 is now at Index 2

Once it has this map of old and new orders, it goes to the $people array and applies the same re-arrangements, so that whatever was in the first spot ("Joe") is now in the second spot, whatever was originally in the second spot ("Bill") is now in the third spot, and so on and so on until $people has been rearranged into the new order.

Now let's say you wanted to order by more than one column. For example, let's say you wanted to start by sorting the "Last Name" column as we've already done with "Income":

$lnames = array();
foreach($people as $person)
{
  $lnames[] = $person["Last Name"];
}
array_multisort($lnames,SORT_ASC,$people);

Since we have two people with the same last name ("Gaites"), let's say we also want to specify the "Income" as a second sorting parameter in descending order, so that the Gaites are listed from richest to poorest! No problem - it's just another array and a couple parameters in our array_multisort call. The final code would look like:

$lnames = array();
$incomes = array();
foreach($people as $person)
{
  $lnames[] = $person["Last Name"];
  $incomes[] = $person["Income"];
}
array_multisort($lnames,SORT_ASC,$incomes,SORT_DESC,$people);

This would sort the $people array to be in the order of Mary Bartley, Bill Gaites, William Gaites, and finally Joe Schmoe.

Okay, to finish, I promised you a shortcut. A while back I came across a snippet of code written by someone named "Ichier" as part of a CMS system or something. This brilliant little function, array_csort, basically handles all the dirty work for you in array_multisort and turns multi-dimensional array sorting into a single line of code:

// Example of using it with our $people array:
$people = array_csort($people,"Last Name",SORT_ASC,"Income",SORT_DESC);

// And here's the function definition:

//coded by Ichier2003
function array_csort()
{
	$args = func_get_args();
	$marray = array_shift($args); 
	$msortline = 'return(array_multisort(';
	$i = 0;
	foreach ($args as $arg) {
		$i++;
		if (is_string($arg)) {
			foreach ($marray as $row) {
				$sortarr[$i][] = $row[$arg];
			}
		} else {
			$sortarr[$i] = $arg;
		}
		$msortline .= '$sortarr['.$i.'],';
	}
	$msortline .= '$marray));';
	eval($msortline);
	return $marray;
}

Here is the home of the function:
http://www.ichier.de/coding/functions/array_csort.html

Enjoy!

Copyright © 2010 - Jonathan Hilgeman. All Rights Reserved. 
0
4,631 Views
gr8gonzoConsultant
CERTIFIED EXPERT

Comments (0)

Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.