Sorter

The Sorter class is made for sorting records where each record is an associative array.  For example, suppose $files is an array of the following form :

$files[] = array( 'name' => 'fred.txt', 'ext' => 'txt', 'size' => 1234 );
$files[] = array( 'name' => 'jane.jpg', 'ext' => 'jpg', 'size' => 412 );
$files[] = array( 'name' => 'readme.txt', 'ext' => 'txt', 'size' => 592 );

You can sort by extension type and size as follows:

$sorter = new Sorter("ext,-size:n");    // sort by extension (alphabetic) and descending size
$sorter->sort($files);

Basically it takes a comma separated list of field names (e.g. "region,city,area"), each of which can optionally be preceded by a “+” (default) or “-” to say whether it is an ascending or descending sort and suffixed with a type letter (e.g “:n”) to say whether the sort is alphabetic (“s” and default), numeric (“n”) or by date (“d”).  The date has to be in a format PHP recognises, but simple timestamps are numeric.

I think this should also work for arrays of numerically indexed arrays, so that "3,-2:d" sorts by column 3 and then column 2 as a date descending, but I’ve not tested this.

the code

class Sorter {
  var $keys;
  function Sorter($keyspec) {
      $this->keys = $this->parseKeys($keyspec);
   }
   function sort( &$arr ) {
      return usort( $arr, array($this,'compare') );
   }
   function parseKeys($keyspec) {
      $keyspecs = explode(",",$keyspec);
      $keys = array();
      foreach ( $keyspecs as $onespec ) {
         if ( $onespec{0} == '-' ) {<
            $onespec = substr($onespec,1);
            $dir = '-';
         } else if ( $onespec{0} == '+' ) {
            $onespec = substr($onespec,1);
            $dir = '+';
         } else {
            $dir = '+';
         }
         $parts = explode(':',$onespec);
         if ( $parts[0] ) {
            switch ( $parts[1] ) {
               case 'i': case 'n':
                  $type = 'n';  break;  // number
               case 'd':
                  $type = 'd';  break;  // date
               case 's': default:
                  $type = 's';  break;  // string
            }
            $keys[] = array( $parts[0], $dir, $type );
         }
      }
      return $keys;
   }
   function compare( $obj1, $obj2 ) {
      foreach( $this-&gt;keys as $key ) {
         list( $name, $dir, $type ) = $key;
         $fld1 = $obj1[$name];  $fld2 = $obj2[$name];<
         switch ( $type ) {
            case 'n':
               $cmp = $fld1 - $fld2;
               break;
            case 'd':
               $time1 = strtotime($fld1);
               $time2 = strtotime($fld2);
               if ( $time1 === false ||  $time1 === -1 ||
                    $time2 === false ||  $time2 === -1 ) {
                        // false PHP 5.1, -1 older PHP
                  $cmp = 0;    // badly formed dates
               } else {
                  $cmp = $time1 - $time2;
               }
            case 's': default:
               $cmp = strcmp($fld1,$fld2);
               break;
         }
         if ( $cmp == 0 ) continue;
         if ( $dir == '-' ) $cmp = - $cmp;
         return $cmp;
      }
      return 0;
   }
}