Posts Tagged ‘quicksort’

Custom order sorting in PHP

Sunday, May 2nd, 2010

Every once in a while you find your self in need of a generic sorting function that orders in a very specific, and non-machine understandable way. Like sorting items from ‘one’ to ‘four’. Instead of building a small subset of those kind of sort operations into the language PHP has usort family (short for user-defined sort). In the background this sort still works as a quicksort, but the developer is tasked with making a function that does the comparison between different elements of the array. It’s up to him to define whether a value is smaller, equal or bigger than another.

So, keeping the ‘one’ to ‘four’ example, I present you with a very simple custom sort function.

function customCompare($a, $b) {
  // We flip the array to get the positional indices 
  $order = array_flip(array('one', 'two', 'three', 'four'));
 
  // Sanity check, $a and $b must exist in the order array
  foreach(array($a, $b) as $arg) {
    if(!array_key_exists($arg, $order))
      trigger_error("`$arg' is not a sortable key");
  }
 
  // The positions in the order array dictate if $a is bigger
  // than $b, as such we can simply subtract the integers
  // from the flipper order array to get an integer value
  // which is smaller, equal or bigger than 0 depending on
  // $a and $b's relative position in the array.
  return $order[$a] - $order[$b];
}
 
// An example array to be sorted
$example = array(
  'four' => 4,
  'one' => 1,
  'three' => 3,
  'two' => 2);
 
// Do the actual sort
uksort($example, 'customCompare');
 
// Dump results
print_r($example);

The dumped array will look like this

Array
(
    [one] => 1
    [two] => 2
    [three] => 3
    [four] => 4
)

If you, like me, like closures and are running PHP 5.3, we can make the compare function a lot nicer and easier to maintain.

function createSorter($order) {
  $order = array_flip($order);
 
  return function($a, $b) use ($order) {
    // Sanity check, again
    foreach(array($a, $b) as $arg) {
      if(!array_key_exists($arg, $order))
        trigger_error("`$arg' is not a sortable key");
    }
 
    return $order[$a] - $order[$b];
  }
}
 
// Sort array $array in the 'one' to 'four' order.
uksort($array, createSorter(
  array('one', 'two', 'three', 'four')));