Delicious Bookmark this on Delicious Share on Facebook SlashdotSlashdot It! Digg! Digg



PHP : Function Reference : Array Functions : usort

usort

Sort an array by values using a user-defined comparison function (PHP 4, PHP 5)
bool usort ( array &array, callback cmp_function )

Example 325. usort() example

<?php
function cmp($a, $b)
{
   if (
$a == $b) {
       return
0;
   }
   return (
$a < $b) ? -1 : 1;
}

$a = array(3, 2, 5, 6, 1);

usort($a, "cmp");

foreach (
$a as $key => $value) {
   echo
"$key: $value\n";
}
?>

The above example will output:

0: 1
1: 2
2: 3
3: 5
4: 6

Example 326.  usort() example using multi-dimensional array

<?php
function cmp($a, $b)
{
   return
strcmp($a["fruit"], $b["fruit"]);
}

$fruits[0]["fruit"] = "lemons";
$fruits[1]["fruit"] = "apples";
$fruits[2]["fruit"] = "grapes";

usort($fruits, "cmp");

while (list(
$key, $value) = each($fruits)) {
   echo
"\$fruits[$key]: " . $value["fruit"] . "\n";
}
?>

When sorting a multi-dimensional array, $a and $b contain references to the first index of the array.

The above example will output:

$fruits[0]: apples
$fruits[1]: grapes
$fruits[2]: lemons

Example 327.  usort() example using a member function of an object

<?php
class TestObj {
   var
$name;

   function
TestObj($name)
   {
       
$this->name = $name;
   }

   
/* This is the static comparing function: */
   
function cmp_obj($a, $b)
   {
       
$al = strtolower($a->name);
       
$bl = strtolower($b->name);
       if (
$al == $bl) {
           return
0;
       }
       return (
$al > $bl) ? +1 : -1;
   }
}

$a[] = new TestObj("c");
$a[] = new TestObj("b");
$a[] = new TestObj("d");

usort($a, array("TestObj", "cmp_obj"));

foreach (
$a as $item) {
   echo
$item->name . "\n";
}
?>

The above example will output:

b
c
d

Related Examples ( Source code ) » usort



Code Examples / Notes » usort

bo

when using usort to refer to a function inside a class i have succesfully used:
usort($myarray,array($this,"cmp"));


steve

When sorting a large multi-dimensional array, I ran into an issue where the array was not being sorted either ascending or descending, even when it started in sorted order.  It turns out that the above note about the callback function returning an integer is true.  My comparison function was just a subtraction between two floating point numbers, and the result being a float produced very unpredictable results.

jeremy swinborne

When I query a DB I usually put my record set inside of a multi-dimentional array.  I finally wrote a program that will allow you to sort your record set by column after you put it in an array.
<?php
$test = array();
$test[0]['name'] = 'jeremy';
$test[0]['email'] = 'lala@fishies.com';
$test[0]['phone'] = '123-123-1234';
$test[0]['trick'] = 'mezopia';
$test[1]['name'] = 'Amanda';
$test[1]['email'] = 'hot@hotmail.com';
$test[1]['phone'] = '123-123-1235';
$test[1]['trick'] = 'youarecool';
$test[2]['name'] = 'john';
$test[2]['email'] = 'wowee@yahoo.com';
$test[2]['phone'] = '123-123-3333';
$test[2]['trick'] = 'goneinanhour';
print_r(columnSort($test, 'name'));
function columnSort($unsorted, $column) {
$sorted = $unsorted;
for ($i=0; $i < sizeof($sorted)-1; $i++) {
 for ($j=0; $j<sizeof($sorted)-1-$i; $j++)
   if ($sorted[$j][$column] > $sorted[$j+1][$column]) {
     $tmp = $sorted[$j];
     $sorted[$j] = $sorted[$j+1];
     $sorted[$j+1] = $tmp;
 }
}
return $sorted;
}
?>


gk

We have written some functions to sort arrays by national ABC's (or by other custom letter-orders).
function sort_hun($array)
{
 usort($array,"huncmp");
 return($array);
}
function intcmp($a,$b,$ALP)
{
 if ($a==$b) return 0;
 $ALPL = strlen($ALP);
 $ap = $bp = -1;
 $i = 0;
 while (($i < $ALPL) and (($ap == -1) or ($bp == -1)))
  {
   if ($ALP[$i] == $a) $ap = $i;
   if ($ALP[$i] == $b) $bp = $i;
   $i++;
  }
 return($ap < $bp) ? -1 :1;
}
function huncmp($astring, $bstring)
{
 $ALP = "AaÁáBbCcDdEeÉéFfGgHhIiÍiJjKkLlMmNnOoÓóÖöÕõPpQqRr".
 "SsTtUuÚúÜüÛûVvWwXxYyZz0123456789!?.()[]=%+-";
 //if equal
 if ($astring == $bstring) return 0;
 //do it on every element
 for ($i = 0; $i<strlen($astring) && $i<strlen($bstring) && $astring[$i]==$bstring[$i]; $i++);
 //if the two strings are the same, the sorter wins
 if ($astring[$i]==$bstring[$i]) return (strlen($astring) > $bstring) ? -1 :1;
 //otherwise depends on the first different char
 return(intcmp($astring[$i],$bstring[$i],$ALP));
}
-----
How to call it? Here is a sample:
$ize = array("jani","béla","ágnes","ÁGNES","zazie","érdekes");
$ize = sort_hun($ize);
(It returns "Array ( [0] => ÁGNES [1] => ágnes [2] => béla [3] => érdekes [4] => jani [5] => zazie )".)


rob

To svenxy AT nospam gmx net
A faster and clearer way of sorting IP addresses:
<?php
$zones =
array('192.168.11',
   '192.169.12',
   '192.168.13',
   '192.167.14',
   '192.168.15',
   '122.168.16',
   '192.168.17'
);
function sort_subnets_ip2long($a, $b) {
   return sprintf("%u", ip2long($a)) - sprintf("%u", ip2long($b));
}
usort($zones, "sort_subnets_ip2long");
?>
Simple testing shows this version is just under twice as fast.
MySQLers: No need to sort this stuff in PHP! Use MySQL's INET_ATON() family of functions to save cycles.
var_dump($zones);


jc dot glombard

To svenxy AT nospam gmx net AND rob at digital-crocus dot com
<?php
$zones =
array('192.168.11',
   '192.169.12',
   '192.168.13',
   '192.167.14',
   '192.168.15',
   '122.168.16',
   '192.168.17'
);
natsort($zones);
?>


todor

To sort multidimentional arrays .... by one key.
function multi_sort($tab,$key){
   $compare = create_function('$a,$b','if ($a["'.$key.'"] == $b["'.$key.'"]) {return 0;}else {return ($a["'.$key.'"] > $b["'.$key.'"]) ? -1 : 1;}');
   usort($tab,$compare) ;
   return $tab ;
}


simen

To sort a list of objects either ascending (a) or descending (d) using key use the function below for comparison.
function property_sort($oa, $ob) {
global $sort_key;
global $sort_dir;
$a = strtolower($oa->$sort_key);
$b = strtolower($ob->$sort_key);
if ($a == $b) {
return 0;
} else if (($a > $b && $sort_dir == "a") || ($a < $b && $sort_dir == "d")) {
return 1;
} else {
return -1;
}
}


oscahie

To markus:
your function can be simplified to:
function SortByName($a, $b) {  
   return strcasecmp($a['name'], $b['name']);
}
usort($test, 'SortByName');
Replace strcasecmp for strcmp if you want case sensitive comparition.


svenxy

This will sort subnets correctly:
<?php
$zones =
array('192.168.11',
'192.169.12',
'192.168.13',
'192.167.14',
'192.168.15',
'122.168.16',
'192.168.17'
);
function sort_subnets ($a, $b) {
   $a_arr = explode('.', $a);
   $b_arr = explode('.', $b);
   foreach (range(0,3) as $i) {
       if ( $a_arr[$i] < $b_arr[$i] ) {
           return -1;
       }
       elseif ( $a_arr[$i] > $b_arr[$i] ) {
           return 1;
       }
   }
   return -1;
}
usort($zones, 'sort_subnets');
print '<pre>';
print_r($zones);
print '</pre>';
?>


josh

This lets you sort an associative multi-dimensional array by multiple key/field names.  Much similiar to the SQL clause ORDER BY.  Enjoy.
function cmp ($a, $b) {
   // Populate this array with your values...
   // Below is the SQL equivalent of
   // select * from blah ORDER BY date desc, type asc, name asc
   $vals = array(
       'date' => 'd',
       'type' => 'a',
       'name' => 'a'
   );
   while(list($key, $val) = each($vals)) {
       if($val == "d") {
           if ($a["$key"] > $b["$key"]) {
               return -1;
           }
           if ($a["$key"] < $b["$key"]) {
               return 1;
           }
       }
       if($val == "a") {
           if ($a["$key"] < $b["$key"]) {
               return -1;
           }
           if($a["$key"] > $b["$key"]) {
               return 1;
           }
       }
   }
}


robert

This is an extension to Todor's function below - it will sort a multidimensional array by a primary key, secondary key and so on.  It uses the same method of passing arguments as array_multisort, including sort order flags (but not sort type flags - arrays are sorted as case-insensitive strings.)
<?php
function array_alternate_multisort()
{
   $arguments = func_get_args();
   $arrays    = $arguments[0];  
   for ($c = (count($arguments)-1); $c > 0; $c--)
   {
       if (in_array($arguments[$c], array(SORT_ASC , SORT_DESC)))
       {
           continue;
       }
       $compare = create_function('$a,$b','return strcasecmp($a["'.$arguments[$c].'"], $b["'.$arguments[$c].'"]);');
       usort($arrays, $compare);
       if ($arguments[$c+1] == SORT_DESC)
       {
           $arrays = array_reverse($arrays);
       }
   }
   return $arrays ;
}
?>
To demonstrate:
<?php
$dir_contents[] = array("is_dir" => 0, "name" => "b.jpg");
$dir_contents[] = array("is_dir" => 1, "name" => "e");
$dir_contents[] = array("is_dir" => 1, "name" => "a");
$dir_contents[] = array("is_dir" => 0, "name" => "d.png");
$dir_contents[] = array("is_dir" => 0, "name" => "c.png");
$dir_sorted = array_alternate_multisort($dir_contents, "is_dir", SORT_DESC, "name", SORT_ASC);
print_r($dir_sorted);
?>
Produces the output:
Array
(
   [0] => Array
       (
           [is_dir] => 1
           [name] => a
       )
   [1] => Array
       (
           [is_dir] => 1
           [name] => e
       )
   [2] => Array
       (
           [is_dir] => 0
           [name] => b.jpg
       )
   [3] => Array
       (
           [is_dir] => 0
           [name] => c.png
       )
   [4] => Array
       (
           [is_dir] => 0
           [name] => d.png
       )
)


alex

This function will sort on more then one values, test and have fun
<pre>
<?php
 $array[0]['name'] = "a";
 $array[0]['id'] = 3;
 $array[1]['name'] = "a";
 $array[1]['id'] = 2;
 $array[2]['name'] = "a";
 $array[2]['id'] = 5;
 $array[3]['name'] = "b";
 $array[3]['id'] = 8;
 $array[4]['name'] = "b";
 $array[4]['id'] = 1;
 $array[5]['name'] = "b";
 $array[5]['id'] = 0;
 $array[6]['name'] = "c";
 $array[6]['id'] = 5;
 $array[7]['name'] = "c";
 $array[7]['id'] = 7;
 $array[8]['name'] = "c";
 $array[8]['id'] = 3;
 print_r($array);
 $sort_array[0]['name'] = "name";
 $sort_array[0]['sort'] = "ASC";
 $sort_array[0]['case'] = TRUE;
 $sort_array[1]['name'] = "id";
 $sort_array[1]['case'] = FALSE;
 sortx($array, $sort_array);
 print_r($array);
 function sortx(&$array, $sort = array()) {
   $function = '';
   while (list($key) = each($sort)) {
     if (isset($sort[$key]['case'])&&($sort[$key]['case'] == TRUE)) {
       $function .= 'if (strtolower($a["' . $sort[$key]['name'] . '"])<>strtolower($b["' . $sort[$key]['name'] . '"])) { return (strtolower($a["' . $sort[$key]['name'] . '"]) ';
     } else {
       $function .= 'if ($a["' . $sort[$key]['name'] . '"]<>$b["' . $sort[$key]['name'] . '"]) { return ($a["' . $sort[$key]['name'] . '"] ';
     }
     if (isset($sort[$key]['sort'])&&($sort[$key]['sort'] == "DESC")) {
       $function .= '<';
     } else {
       $function .= '>';
     }
     if (isset($sort[$key]['case'])&&($sort[$key]['case'] == TRUE)) {
       $function .= ' strtolower($b["' . $sort[$key]['name'] . '"])) ? 1 : -1; } else';
     } else {
       $function .= ' $b["' . $sort[$key]['name'] . '"]) ? 1 : -1; } else';
     }
   }
   $function .= ' { return 0; }';
   usort($array, create_function('$a, $b', $function));
 }
?>
</pre>


johan_land

These functions will sort an array by more than one element. The elements to sort by is specified in $sortArr. If you eg. want to sort by $destArr[]["sortBy2"][0] you add '["sortBy2"][0]' to $sortArr. Use it as much as you want!! If you modify it, please let me know...
//The functions
function compare($a, $b) {
return cmpRec(0, $a, $b);
}
function cmpRec($i, $a, $b) {
global $sortArr;
if($i == sizeof($sortArr)) {
return 0;
}
$avalue = '$avalue = $a'.$sortArr[$i].';';
$bvalue = '$bvalue = $b'.$sortArr[$i].';';
eval($avalue);
eval($bvalue);
if($avalue == $bvalue) {
return cmpRec($i+1, $a, $b);
} else {
return ($avalue > $bvalue) ? (1) : (-1);
}
}
//For testing the functions
$destArr = array( array( "sortBy1" => 2, "sortBy2" => array( "Fish", "eat seafood" )), array( "sortBy1" => 1, "sortBy2" => array( "Lions", "eat everything" )), array( "sortBy1" => 1, "sortBy2" => array( "Bird", "eat birdfood" )) );
$sortArr = array( '["sortBy1"]', '["sortBy2"][0]' );
print_r($destArr);
usort($destArr, "compare");
reset($destArr);
print_r($destArr);


steveno_spam

The sort functions do nothing if identical keys exist.  Of course, you shouldn't have identical keys anyway, but this just might save someone else the oodles of time it took me to figure out while using multi-dimentional arrays:
class myArticleList {
// ... //
 function &getList () {
   // works
       $a = array (
                  "articleList1" => array ( "2", "3" ),
                  "articleList2" => array ( "3" , "4")
                );
   usort( $a, array( $this, "compareByTitle") );
     
   // doesn't work
       $b = array (
                  "articleList" => array ( "2", "3" ),
                  "articleList" => array ( "3" , "4")
                );
   usort( $b, array( $this, "compareByTitle") );
 }
 function compareByTitle( $a, $b ) {
 // ... //
 }
}


sid_m

The sample function for having the sort function be a class method has a comment identifying the sorting function as static, however the function does not have the static keyword. In PHP 5 (at least as configured on my server), usort will fail, identifying the method as non-static. Preceding the function keyword with the static keyword fixed the problem.

dmhouse

The easiest way to compare two integers is just to take the second away from the first. For example, say you wanted to sort by an integer property of an object. Your comparison function would look like this:
<?php
function compare_counts($a, $b) {
 return $a->count - $b->count;
}
?>
This works because you don't necessarily have to return -1, 0 or 1, the manual states any integer less than, equal to or greater than 0 is OK.


raveler

The array_alternate_multisort function written by robert below doesn't work. There are several bugs in the code and it doesn't work when sorting by multiple keys because the order of the first key isn't taken into account when sorting by the second key and so on. Also, because robert uses strcasecmp the algorithm doesn't work properly with floats or other variable types. Here's the improved version:
<?php
function SortArray() {
$arguments = func_get_args();
$array = $arguments[0];
$code = '';
for ($c = 1; $c < count($arguments); $c += 2) {
if (in_array($arguments[$c + 1], array("ASC", "DESC"))) {
$code .= 'if ($a["'.$arguments[$c].'"] != $b["'.$arguments[$c].'"]) {';
if ($arguments[$c + 1] == "ASC") {
$code .= 'return ($a["'.$arguments[$c].'"] < $b["'.$arguments[$c].'"] ? -1 : 1); }';
}
else {
$code .= 'return ($a["'.$arguments[$c].'"] < $b["'.$arguments[$c].'"] ? 1 : -1); }';
}
}
}
$code .= 'return 0;';
$compare = create_function('$a,$b', $code);
usort($array, $compare);
return $array;
}
?>


mharrodine

Thankyou to franky at iname dot com for his solution to my problem although i'd like to clarify something because the use for this isn't entirely obvious at first glance. I like to define my arrays to look like tables in a database or spreadsheet as follows (it looks tidy is the only reason!):
Row(s)  Column(s) ------------->
  |        $array[0] = array("1", "2","3");
  |        $array[1] = array("1", "2","3");
  \/       $array[2] = array("1", "2","3");
This "array of arrays" seems to behave differently to normal associative or multi-dimension arrays when sorting but using franky's routine....
function cmp ($a, $b)
{  
global $w_o;
if ($a[$w_o] == $b[$w_o]) return 0;
return ($a[$w_o] < $b[$w_o]) ? -1 : 1;
}
...you simply specify the column you want to sort by defining $w_o and call "usort($my_array,"cmp");". This might seem obvious to some people but wasn't to me and I hope this helps others in the same situation. Thanks....


jasper

reinhard at ess dot co dot at: you need to look at the natsort() function.

martin # classaxe com <><

Refinement of arfsort to allow per-field sort direction setting:
<?
function arfsort( $a, $fl ){
 $GLOBALS['__ARFSORT_LIST__'] = $fl;
 usort( $a, 'arfsort_func' );
 return $a;
}
// extended to allow sort direction per field sorted against
function arfsort_func( $a, $b ){
 foreach( $GLOBALS['__ARFSORT_LIST__'] as $f ) {
   switch ($f[1]) { // switch on ascending or descending value
     case "d":
       $strc = strcmp( strtolower($b[$f[0]]), strtolower($a[$f[0]]) );
       if ( $strc != 0 ){
         return $strc;
       }
     break;
     default:
       $strc = strcmp( strtolower($a[$f[0]]), strtolower($b[$f[0]]) );
       if ( $strc != 0 ){
         return $strc;
       }
     break;
   }
 }
 return 0;
}
// Just prints out record contents
function show($test) {
 foreach ($test as $key=>$row) {
   print "<li>($key) ".$row['fruit']." is ".$row['type']." at ".$row['time']." on ".$row['date']."</li>\n";
 }
 print "<hr>";
}
// Example usage
$test = array(
 array( 'fruit' => 'Lemon', 'type' => 'sour', 'date' => '2006-05-16', 'time' => '12:36'),
 array( 'fruit' => 'Banana','type' => 'sweet','date' => '2006-05-17','time' => '12:35'),
 array( 'fruit' => 'Apple', 'type' => 'sweet','date' => '2006-05-17','time' => '12:35'),
 array( 'fruit' => 'green apple', 'type' => 'sour', 'date' => '2006-05-16', 'time' => '12:37')
);
show($test);
$order_arr =
 array(
   array('date','d'), // d means decending - swap for 'a' to see effect
   array('time','a'),
   array('fruit','a')
 );
$sorted = arfsort( $test, $order_arr);
show($sorted);
?>


06-apr-2001 12:37

RE comparator functions within classes:
On PHP4.04, I found that the comparator was completely ignored within the class even when using the usort($myArray, "\$this->sortFunction"); method above.  The usort( $myArray, array($this,"sortFunction")) worked. Haven't tested on PHP3.


ulrichunderscorealexis

Please note that:
- the HTML entities should be replaced by their accented counterparts;
- the $patterns arrays have been split for display convenience.
<?php
/* returns the collated string (with no accent or with the matching string
* replacement) in lower case according to that language rule.
* The aim is to alphabetically sort lists of strings with characters from
* other languages but using collation rules of that given language
* (ISO 639-1 code).
* Latin-derived alphabets are supported (currently English, French and
* German)
* rules source: http://en.wikipedia.org/wiki/Latin_alphabet
*/
function collatedLower($string, $lang=null) {
  switch ($lang) {
      case 'de':
          // German
$patterns = array(
"/&aacute;|&agrave;|&acirc;|&atilde;|&aring;|
&Aacute;|&Agrave;|&Acirc;|&Atilde;|&Aring;/",
"/&auml;|&aelig;|&Auml;|&AElig;/",
"/&ccedil;|&Ccedil;/",
"/&eacute;|&egrave;|&ecirc;|&euml;|
&Eacute;|&Egrave;|&Ecirc;|&Euml;/",
"/&igrave;|&iacute;|&icirc;|&iuml;|
&Igrave;|&Iacute;|&Icirc;|&Iuml;/",
"/&ntilde;|&Ntilde;/",
"/&ograve;|&oacute;|&ocirc;|&otilde;|&oslash;|
&Ograve;|&Oacute;|&Ocirc;|&Otilde;|&Oslash;/",
"/&ouml;|&oelig;|&Ouml;|&OElig;/",
"/&szlig;/",
"/&ugrave;|&uacute;|&ucirc;|
&Ugrave;|&Uacute;|&Ucirc;/",
"/&uuml;|&Uuml;/",
"/&yacute;|&yuml;|&Yacute;/");
          $replace = array('a', 'ae', 'c', 'e', 'i', 'n', 'o', 'oe', 'ss', 'u', 'ue', 'y');
          break;
      default:
          // 'en' (English), 'fr' (French)
          $patterns = array(
"/&aacute;|&agrave;|&acirc;|&atilde;|&auml;|&aring;|
&Aacute;|&Agrave;|&Acirc;|&Atilde;|&Auml;|&Aring;/",
"/&aelig;|&AElig;/",
"/&ccedil;|&Ccedil;/",
"/&eacute;|&egrave;|&ecirc;|&euml;|
&Eacute;|&Egrave;|&Ecirc;|&Euml;/",
"/&igrave;|&iacute;|&icirc;|&iuml;|
&Igrave;|&Iacute;|&Icirc;|&Iuml;/",
"/&ntilde;|&Ntilde;/",
"/&ograve;|&oacute;|&ocirc;|&otilde;|&ouml;|&oslash;|
&Ograve;|&Oacute;|&Ocirc;|&Otilde;|&Ouml;|&Oslash;/",
"/&oelig;|&OElig;/",
"/&szlig;/",
"/&ugrave;|&uacute;|&ucirc;|&uuml;|
&Ugrave;|&Uacute;|&Ucirc;|&Uuml;/",
"/&yacute;|&yuml;|&Yacute;/");
          $replace = array('a', 'ae', 'c', 'e', 'i', 'n', 'o', 'oe', 'ss', 'u', 'y');
          break;
      }
  return preg_replace($patterns,$replace,$string);
}
// English
function compareAccents_en($a, $b) {
  return compareAccents($a, $b, 'en');
}
// French
function compareAccents_fr($a, $b) {
  return compareAccents($a, $b, 'fr');
}
// German
function compareAccents_de($a, $b) {
  return compareAccents($a, $b, 'de');
}
/*
* comparison function to group together accented letters with
* their lower-case non-accented counterparts
* the $lang parameter enables sorting rules for that language
* (ISO 639-1 code)
*/
function compareAccents($a, $b, $lang=null) {
  $anew = strtolower(collatedLower($a,$lang));
  $bnew = strtolower(collatedLower($b,$lang));
  if ($anew < $bnew) return -1;
  if ($anew > $bnew) return 1;
  return 0;
}
// usage:
usort($myList,'compareAccents_fr');
// to be compared to:
setlocale(LC_COLLATE,'fr');
usort($myList, 'strcoll');
?>


webmaster

Needed a quick, fairly uncluttered way of sorting an array of objects by a certain object attribute, so here's what I came up with. Uses one global array and usort(). Also accepts an optional sort direction argument (CSORT_ASC = sort ascending, CSORT_DESC = sort descending). Use it like this:
(assuming $myarray is your array, "index" is the attribute you want to sort by, and you want to sort in descending order)
     csort($myarray, "index", CSORT_DESC);
Hope this is of use to someone. Probably a better way to pull this off, but this works alright.
define("CSORT_ASC",     1);
define("CSORT_DESC",     -1);
function csort_cmp(&$a, &$b)
{
global $csort_cmp;
if ($a->$csort_cmp['key'] > $b->$csort_cmp['key'])
return $csort_cmp['direction'];
if ($a->$csort_cmp['key'] < $b->$csort_cmp['key'])
return -1 * $csort_cmp['direction'];
return 0;
}
function csort(&$a, $k, $sort_direction=CSORT_ASC)
{
global $csort_cmp;
$csort_cmp = array(
'key'   => $k,
'direction'     => $sort_direction
);
usort($a, "csort_cmp");
unset($csort_cmp);
}


derek

Needed a date sort and I didn't know if one was available so I wrote one. Maybe it'll help someone:
function DateSort($a,$b,$d="-") {
if ($a == $b)
{ return 0; }
else {  //Convert into dates and compare
list($am,$ad,$ay)=split($d,$a);
list($bm,$bd,$by)=split($d,$b);
if (mktime(0,0,0,$am,$ad,$ay) < mktime(0,0,0,$bm,$bd,$by))
{ return -1; }
else { return 1; }
}
}
$d is the delimeter


reinhard

maybe someone can use this.
with that compare-function you can get this order:
string1, string2, ..., string10, string11, ...string22...
else the functions make something like
string10,string11,...string1,string20....
or
string1,string10,string11,string2,string20...
it's not the best solution, but works for my purpose...
<?
function cmp($a,$b){
  list($a)=explode(".",$a);
  list($b)=explode(".",$b);
  $s=0;
  for($i=0;$i<=strlen($a);++$i){
     if(is_numeric($a[$i])&&is_numeric($b[$i])){
        $n=1;
        $anum=$bnum=0;
        while((is_numeric($a[$i])||is_numeric($b[$i]))&&
                   (strlen($a)>=$i||strlen($b)>=$i)){
           if(is_numeric($a[$i])&&$i<=strlen($a)) $anum=$a[$i]+$anum*$n;
           if(is_numeric($b[$i])&&$i<=strlen($b)) $bnum=$b[$i]+$bnum*$n;
           $n*=10;
           ++$i;
        }
        if($n>1) --$i;
        if($anum!=$bnum) return $anum<$bnum?-1:1;
     }elseif($a[$i]!=$b[$i]) return $a[$i]<$b[$i]?-1:1;
  }
}
?>
use it like this:
<?
usort($array,"cmp");
?>


guenther dot stadler

Just something i stumbled about right now:
If the array consists of just one elemtent, the user-function is not called at all.
Although this surely makes sense, think of it if you want to use your order-function for adding extra data to your arrays...


xnoguer

just for debugging purposes, usort does not complain if its argument function is not defined, i.e.:
usort($my_array,"non existant function");
will not do anything...


sydney

Instead of doing  :
$strc = strcmp( strtolower($a[$f]), strtolower($b[$f]) );
you could do this :
$strc = strcasecmp( $a[$f], $b[$f] );
which is more efficient and is does case insensitive comparison according to the current locale.


sean

In order to get a proper text sorting I have changed the arfsort() function submitted by jonathan(at)inetz(dot)com.
The one line I have changed is:
<?php
$strc = strcmp( $a[$f], $b[$f] );
?>
to
<?php
$strc = strcmp( strtolower($a[$f]), strtolower($b[$f]) );
?>
It can now sort correctly for alphabetization.  Capital letters where getting in the way.
Sean


jfren484

If you've used ADO before, you may have used the Sort property on a recordset.  It's very powerful - you can add a nice ORDER BY clause after getting the recordset from the database.  It's especially helpful if you want to show a list on a web page and make the column headings links that cause the list to resort on that column.
I wanted to do the same thing with mysql recordsets, but it looks like you have to have the ORDER BY in your query.  I re-worked the example from johan_land at yahoo dot com above for sorting multidimensional arrays.  When I get a mysql recordset, I create an array with all of the records like this:
$aaRows[] = array();
if (mysql_num_rows($rsRows) > 0)
while ($aRow = mysql_fetch_array($rsRows, MYSQL_ASSOC))
$aaRows[] = $aRow;
At this point $aaRows is an array of arrays - it's a numeric-indexed array containing records from the recordset, which themselves are associative arrays.  The following code takes the array of records as the first parameter, and an array of fields to sort - each field is an associative array, where 'name' is the db field name, and 'dir' is the direction to sort.  If dir is 'DESC' (case-insensitive), it will sort descending.  Any other value (including not setting one) will cause it to sort ascending.
function sortRows(&$a_aaRows, $a_aaSortCriteria)
{
GLOBAL $g_aaSortArray;
function compare($a_aRow1, $a_aRow2, $a_lField = 0)
{
GLOBAL $g_aaSortArray;
$lCompareVal = 0;
if ($a_lField < count($g_aaSortArray))
{
$sSortFieldName = $g_aaSortArray[$a_lField]['name'];
$sSortFieldDir = $g_aaSortArray[$a_lField]['dir'];
$vValue1 = eval('return $a_aRow1[' . $sSortFieldName . '];');
$vValue2 = eval('return $a_aRow2[' . $sSortFieldName . '];');
if ($vValue1 == $vValue2)
$lCompareVal = compare($a_aRow1, $a_aRow2, $a_lField + 1);
else
{
$lCompareVal = $vValue1 > $vValue2 ? 1 : -1;
if (strtolower(substr($sSortFieldDir, 0, 4)) == 'desc')
$lCompareVal = -$lCompareVal;
}
}
return $lCompareVal;
}
$g_aaSortArray = $a_aaSortCriteria;
usort($a_aaRows, 'compare');
}
When I call it it looks something like this:
sortRows($aaRows, array(array('name' => 'STATE', 'dir' => 'ASC'), array('name' => 'CITY', 'dir' => 'DESC')));


tim

If you want to sort an array of strings by the length of its values, you can write a better cmp-function than manually comparing string lengths with if-statements. Unfortunately, I see this bad technique in other people's code. I prefer using this:
<?php
function length_cmp( $a, $b ) {
return strlen($a)-strlen($b) ;
}
?>
This function sorts the array in ascending order. If you want to sort descending, simply swap $a and $b either in the parameters or in the subtraction.
-Tim


mkr

If you want to sort an array according to another array acting as a priority list, you can use this function.
function listcmp($a, $b)
{
 global $order;
 foreach($order as $key => $value)
   {
     if($a==$value)
       {
         return 0;
         break;
       }
     if($b==$value)
       {
         return 1;
         break;
       }
   }
}
$order[0] = "first";
$order[1] = "second";
$order[2] = "third";
$array[0] = "second";
$array[1] = "first";
$array[2] = "third";
$array[3] = "fourth";
$array[4] = "second";
$array[5] = "first";
$array[6] = "second";
usort($array, "listcmp");
print_r($array);


kristen

I have a class with a bunch of functions that work with an SQL database. I am working with calendar dates that occur more than once (like once a week for 2 months), so I have to generate the date in php after the SQL. But I wanted to sort the events based on the date, so I tried using usort, but was unable to get it to work, because it didn't think that my sorting function was defined (even though it was in the same class). I have a separate class to store the data, that has a variable called Start which is a PHP date object.
So here is how I got it to work. First I created a temporary function, that returned the value of a string comparison of the timestamps for the dates. Then I used that temporary function for the usort. Enjoy.
$tempfunction = create_function('$event1,$event2',
'return strcmp($event1->Start[0],$event2->Start[0]);');
usort($data,$tempfunction);


skrebbel

here's another recursive multisort, however a clean and fast one that is class-based (but works as well outside classes) and does not uglify your global namespace at all. note that strnatcmp is used, but one could use something else of course.
btw, for arrays in which the rows/columns are 'swapped', use array_multisort().
class Banana {
   var $aData;//the array we want to sort.
   var $aSortkeys;//the order in which we want the array to be sorted.
   function _sortcmp($a, $b, $i=0) {
       $r = strnatcmp($a[$this->aSortkeys[$i]],$b[$this->aSortkeys[$i]]);
       if($r==0) {
           $i++;
           if ($this->aSortkeys[$i]) $r = $this->_sortcmp($a, $b, $i+1);
       }
       return $r;
   }
   function sort() {
       if(count($this->aSortkeys)) {
           usort($this->aData,array($this,"_sortcmp"));
       }
   }
}
$B = new Banana;
$B->aData = array(
   array("name" => "hank", "headsize" => "big", "age" => 32),
   array("name" => "frank", "headsize" => "huge", "age" => 36)
);
$B->aSortkeys = array("age","name");
$B->sort();
sorry for the ugly indenting, but i couldn't get it any better in this note adder thingo.


jonathan

Here's a variation on the multi-dimensional sorts above, but with more flexibility and speed.  Note that this version only leverages strcmp() to compare array values, which is sufficient for most cases.
/**
* arfsort() - (AR)ray (F)ield Sort.
* Sort a multi-dimensional array according
* to a list of fields.
* @param $a The array to sort
* @param $fl Field list (in order of importance)
*/
function arfsort( $a, $fl )
{
&nbsp;$GLOBALS['__ARFSORT_LIST__'] = $fl;
 usort( $a, 'arfsort_func' );
 return $a;
}
/**
* Internal sorting function for arfsort()
*/
function arfsort_func( $a, $b )
{
 foreach( $GLOBALS['__ARFSORT_LIST__'] as $f )
 {
   $strc = strcmp( $a[$f], $b[$f] );
   if ( $strc != 0 )
   {
     return $strc;
   }
 }
 return 0;
}
// Example usage
$test = array(
 array(
   'fruit' => 'apple',
   'type' => 'sweet'
 ),
 array(
   'fruit' => 'green apple',
   'type' => 'sour'
 ),
 array(
   'fruit' => 'lemon',
   'type' => 'sour'
 )
);
$sorted = arfsort( $test, array( 'type', 'fruit' ) );
// Returned array should be sorted
// with the green apple data first, then the
// lemon, then the apple.
Awesome for sorting SQL result sets.


prozac

Here is a simple example of converting a timestamp date("U") into a date. This sorts by that day and then by string value alphabetically.
I hope it saves someone some time... Happy PHP'in!
//data to sort
$shared[0]["page_id"] = "2025731470";
$shared[1]["page_id"] = "2025731450";
$shared[2]["page_id"] = "1025731456";
$shared[3]["page_id"] = "1025731460";
$shared[0]["username"] = "larry";
$shared[1]["username"] = "alvin";
$shared[2]["username"] = "garth";
$shared[3]["username"] = "harvy";
//function to convert timestamp to date
function convert_timestamp($timestamp){
$limit=date("U");
$limiting=$timestamp-$limit;
return date ("Ymd", mktime (0,0,$limiting));
}
//comparison function
function cmp ($a, $b) {
$l=convert_timestamp($a["page_id"]);
$k=convert_timestamp($b["page_id"]);
if($k==$l){
return strcmp($a["username"], $b["username"]);
}else{
return strcmp($k, $l);
}
}
//sort array
usort($shared, "cmp");
//display sorted info
while (list ($key, $value) = each ($shared)) {
   echo "\$shared[$key]: ";
echo $value["page_id"];
echo " username: ";
echo $value["username"];
echo "<break_tag>";
}
//This will output:
$shared[0]: 2025731450   username: alvin
$shared[1]: 2025731470   username: larry
$shared[2]: 1025731456   username: garth
$shared[3]: 1025731460   username: harvy


markus dot meier

gk at lka dot hu:
I've removed a few errors and moved both functions into one. The line with the prefix-check did'nt work right.
<?php
   function CompareUmlaut($astring, $bstring) {
       $ALP = "AaÁáBbCcDdEeÉéFfGgHhIiÍiJjKkLlMmNnOoÓóÖöÕõPpQqRrSs" .  
              "TtUuÚúÜüÛûVvWwXxYyZz0123456789!?.()[]=%+-"
       // find first differing char
       $aLen = strlen($astring); $bLen = strlen($bstring);
       for ($i=0; $i<$aLen && $i<$bLen && $astring[$i]==$bstring[$i]; $i++);
       // if one string is the prefix of the other one, the shorter wins
       if ($i == $aLen || $i == $bLen) return (strlen($astring) < strlen($bstring)) ? -1 : 1;
       
       // otherwise depends on the first different char
       $ALPL = strlen($ALP);
       $ap = $bp = -1;
       $j = 0;
       while (($j < $ALPL) && (($ap == -1) || ($bp == -1))) {
           if ($ALP[$j] == $astring[$i]) $ap = $j;
           if ($ALP[$j] == $bstring[$i]) $bp = $j;
           $j++;
       }
       return($ap < $bp) ? -1 : 1;
?>


number type ???

function arfsort($a,$fl){
 $GLOBALS['__ARFSORT_LIST__'] = $fl;
 usort($a,'arfsort_func');
 return $a;
}
function arfsort_func($a,$b) {
 foreach ($GLOBALS['__ARFSORT_LIST__'] as $f) {
  switch ($f[1]) { // switch on ascending or descending value
    case "d":
      ## Add number type sort
      if (is_numeric($a[$f[0]])) {
      if ($b[$f[0]] < $a[$f[0]]) $strc = -1;
      else if ($b[$f[0]] > $a[$f[0]]) $strc = 1;
      else $strc = 0;
      } else {
        $strc = strcmp($b[$f[0]],$a[$f[0]]);
      }
      if ( $strc != 0 ){
        return $strc;
      }
    break;
    default:
      if (is_numeric($a[$f[0]])) {
    if ($b[$f[0]] > $a[$f[0]]) $strc = -1;
    else if ($b[$f[0]] < $a[$f[0]]) $strc = 1;
    else $strc = 0;
    } else {
      $strc = strcmp($a[$f[0]],$b[$f[0]]);
    }
      if ($strc != 0){
        return $strc;
      }
    break;
  }
 }
 return 0;
}


stephen in koto

For using usort inside a method in an object, where the callback sort method is in the same object, this works:
usort($arr_to_sort, array($this, "sort_terms_by_criteria"));
If you wish to keep elements in their original or reverse order if they are the same length, just don't return zero for items that compare the same. Return a 1 or -1, as appropriate.


franky

For sort multi-array by specific index
function cmp ($a, $b)
{  
global $w_o;
if ($a[$w_o] == $b[$w_o]) return 0;
return ($a[$w_o] < $b[$w_o]) ? -1 : 1;
}
# the index is the second element of
# each row
$w_o =1;
usort($my_arry_info,"cmp");


me_islandnet_com

First off just let me say thanks to skrebbel at operamail dot com for his excellent Banana class.  I've expanded it so that you can specify an ordering sequence (ASC, DESC) on each of the fields.
<?php
class mdasort {
   var $aData;//the array we want to sort.
   var $aSortkeys;//the order in which we want the array to be sorted.
   
   function _sortcmp($a, $b, $i=0) {
       $r = strnatcmp($a[$this->aSortkeys[$i][0]],$b[$this->aSortkeys[$i][0]]);
       if ($this->aSortkeys[$i][1] == "DESC") $r = $r * -1;
       if($r==0) {
           $i++;
           if ($this->aSortkeys[$i]) $r = $this->_sortcmp($a, $b, $i);
       }
       return $r;
   }

   function sort() {
       if(count($this->aSortkeys)) {
           usort($this->aData,array($this,"_sortcmp"));
       }
   }
}
$B = new mdasort;
$B->aData = array(
   array("name" => "hank", "headsize" => "small", "age" => 32),
   array("name" => "sade", "headsize" => "petit", "age" => 36),
   array("name" => "hank", "headsize" => "large", "age" => 33),
   array("name" => "sade", "headsize" => "large", "age" => 32),
   array("name" => "john", "headsize" => "large", "age" => 32),
   array("name" => "hank", "headsize" => "small", "age" => 36),
   array("name" => "hank", "headsize" => "small", "age" => 40)
);
$B->aSortkeys = array(
       array('name','ASC'),
       array('headsize','DESC'),
       array('age','ASC'),
   );
$B->sort();
foreach ( $B->aData as $display_row )
{
echo $display_row['name'] . ', ';
echo $display_row['headsize'] . ', ';
echo $display_row['age'];
echo '
';
}
?>


graham

Example 3 above does not work with 4.0.4pl1, unless you write the uasort line as follows:
uasort($a, array ($a[0], "cmp_obj"));
(I assume any instance of the object: a[0] or a[1] or a[2] would work as well)
It does not work at all with associative arrays of objects.


16-jun-2005 10:10

Case insensitive sort with foreign letters.
<?php
$orig[] = "Abba";
$orig[] = "pappa";
$orig[] = "öiorhkl";
$orig[] = "öäåÖÄÅ1";
$orig[] = "ÖäåÖÄÅ1";
function cmp($a, $b)
{
$a=html_entity_decode(strtolower(htmlentities($a)));
$b=html_entity_decode(strtolower(htmlentities($b)));
  if ( $a == $b ) {
      return 0;
  }
  return ($a < $b) ? -1 : 1;
}
usort($orig,"cmp");
/*
returns:
Array
(
   [0] => Abba
   [1] => pappa
   [2] => öiorhkl
   [3] => ÖäåÖÄÅ1
   [4] => öäåÖÄÅ1
)
*/
?>


sreid

As the manual says, "If two members compare as equal, their order in the sorted array is undefined." This means that the sort used is not "stable" and may change the order of elements that compare equal.
Sometimes you really do need a stable sort. For example, if you sort a list by one field, then sort it again by another field, but don't want to lose the ordering from the previous field. In that case it is better to use usort with a comparison function that takes both fields into account, but if you can't do that then use the function below. It is a merge sort, which is guaranteed O(n*log(n)) complexity, which means it stays reasonably fast even when you use larger lists (unlike bubblesort and insertion sort, which are O(n^2)).
function mergesort(&$array, $cmp_function = 'strcmp') {
   // Arrays of size < 2 require no action.
   if (count($array) < 2) return;
   // Split the array in half
   $halfway = count($array) / 2;
   $array1 = array_slice($array, 0, $halfway);
   $array2 = array_slice($array, $halfway);
   // Recurse to sort the two halves
   mergesort($array1, $cmp_function);
   mergesort($array2, $cmp_function);
   // If all of $array1 is <= all of $array2, just append them.
   if (call_user_func($cmp_function, end($array1), $array2[0]) < 1) {
       $array = array_merge($array1, $array2);
       return;
   }
   // Merge the two sorted arrays into a single sorted array
   $array = array();
   $ptr1 = $ptr2 = 0;
   while ($ptr1 < count($array1) && $ptr2 < count($array2)) {
       if (call_user_func($cmp_function, $array1[$ptr1], $array2[$ptr2]) < 1) {
           $array[] = $array1[$ptr1++];
       }
       else {
           $array[] = $array2[$ptr2++];
       }
   }
   // Merge the remainder
   while ($ptr1 < count($array1)) $array[] = $array1[$ptr1++];
   while ($ptr2 < count($array2)) $array[] = $array2[$ptr2++];
   return;
}


gfaron

As a correction to the piece of code donated by  ccunning@math.ohio-state.edu above, this function will randomize an array passed by reference.  The previous version decreased the length of the array by one.
Greg
<?php
function myshuffle($array)
 {
 mt_srand((double) microtime() * 1000000);
 $num = count($array);
 for ($i = 0; $i < $num; $i ++)
   {
   $n = mt_rand(0, $num - 1);
   // Swap the data.
   $temp = $array[$n];
   $array[$n] = $array[$i];
   $array[$i] = $temp;
   } // ends for
 } // ends function myshuffle(&array)
// Test the results.
$array = array(1,2,3,4,5,6,7,8,9,10);
myshuffle(&$array);
while (list(,$var)=each($array))
 echo $var . " ";
?>


15-mar-2007 01:12

A simple way of having reversed sort order from usort() function without using array_reverse() is to change "greater than" to "lesser that" sign (or vice versa) in the compare function.

tim

A couple examples here advocate the use of 'create_function' for sorting, which is tempting to use because of the limitations of usort. But beware this method -- the function created will NOT be freed at the end of the sorting routine, which creates a memory leak. For this reason, this method should probably never be used.
If you need multi-key or other complex sorting, the fundamental problem is passing in parameters into the comparison routine. I suggest creating a global variable (_array_key_multisort_parameters or something long), and controlling the comparison routine in that way.


markus dot meier

@Jeremy Swinborne:
The same can be achieved by usort and will be much faster, too:
<?php
   function SortByName($a, $b) {
       if ($a['name'] == $b['name']) return 0;
       return return ($a['name'] < $b['name']) ? -1 : 1;
   }
   usort($test, 'SortByName');
?>


rpmpubli

(I've browsed the docummented notes and I haven't found this note, so there I go):
Please note that, when sorting associative arrays (i.e. non-numeric indexes), these will be lost and replaced by a regular numeric index starting at 0.


lite

<?php
//
class __partial {
   var $args;
   var $fn;
   
   function __partial($args) {
       $this->fn = array_shift ($args);
       $this->args = $args;
   }
   
   function right() {
       $args = func_get_args ();
       array_splice ($args, count($args), 0, $this->args);
       return call_user_func_array ($this->fn, $args);
   }
   
   function left() {
       $args = func_get_args ();
       array_splice ($args, 0, 0, $this->args);
       return call_user_func_array ($this->fn, $args);
   }
}
function rcurry () {
   $closure = &new __partial (func_get_args ());
   return array ($closure, 'right');
}
function lcurry () {
   $closure = &new __partial (func_get_args ());        
   return array ($closure, 'left');
}
function compare($a, $b, $key) {
   if ($a[$key] == $b[$key]) {
       return 0;
   }
   return ($a[$key] > $b[$key]) ? +1 : -1;
}
function sortOn ($array, $key) {        
   usort ($array, rcurry('compare', $key));
   return $array;
}
$array = array (array ('key' => 1), array ('key' => 5), array ('key' => 3));
print_r (sortOn ($array, 'key'));
?>


arjini

<?php
/*
This is a class I built to sort parent/child relationships of array elements.
I post this here, because thanks to usort, you can also assign an explicit order
to the elements.
I first built this to sort categories and subcategories.
My database has four fields. category_id, parent_id, category_name, category_position
$rows is an associative array from the database with my categories.
*/
$rows = array( 0=>array('category_id'=>1,'parent_id' =>3,'category_name' =>'Second Child','category_position'=>2),
1=>array('category_id' =>2,'parent_id' =>0,'category_name' =>'Second Parent','category_position'=>2),
2=>array('category_id' =>3,'parent_id' =>0,'category_name' =>'First Parent','category_position'=>1),
3=>array('category_id' =>4,'parent_id' =>0,'category_name' =>'Third Parent','category_position'=>3),
4=>array('category_id' =>5,'parent_id' =>3,'category_name' =>'First Child','category_position'=>1),
5=>array('category_id' =>6,'parent_id' =>5,'category_name'=>'Second Sub-Child','category_position'=>2),
6=>array('category_id' =>7,'parent_id' =>5,'category_name' =>'First Sub-Child','category_position'=>1)
);
$ordered = chain('category_id', 'parent_id', 'category_position', $rows);
foreach($ordered as $item)
{
echo str_repeat('------', $item['indent']).$item['category_name'].'
';
}
/*
Output
First Parent
------First Child
------------First Sub-Child
------------Second Sub-Child
------Second Child
Second Parent
Third Parent
You can have it only return a certain "branch" if you specify a root_id.
Aditionally, you can keep it by going to deep by specifying max-level.
Hope someone finds this useful, I sure would have liked to see this here a week ago.
Ah yes... and the code that runs it.
*/
function chain($primary_field, $parent_field, $sort_field, $rows, $root_id=0, $maxlevel=25)
{
$c = new chain($primary_field, $parent_field, $sort_field, $rows, $root_id, $maxlevel);
return $c->chain_table;
}
class chain
{
var $table;
var $rows;
var $chain_table;
var $primary_field;
var $parent_field;
var $sort_field;

function chain($primary_field, $parent_field, $sort_field, $rows, $root_id, $maxlevel)
{
$this->rows = $rows;
$this->primary_field = $primary_field;
$this->parent_field = $parent_field;
$this->sort_field = $sort_field;
$this->buildChain($root_id,$maxlevel);
}
function buildChain($rootcatid,$maxlevel)
{
foreach($this->rows as $row)
{
$this->table[$row[$this->parent_field]][ $row[$this->primary_field]] = $row;
}
$this->makeBranch($rootcatid,0,$maxlevel);
}

function makeBranch($parent_id,$level,$maxlevel)
{
$rows=$this->table[$parent_id];
foreach($rows as $key=>$value)
{
$rows[$key]['key'] = $this->sort_field;
}

usort($rows,'chainCMP');
foreach($rows as $item)
{
$item['indent'] = $level;
$this->chain_table[] = $item;
if((isset($this->table[$item[$this->primary_field]])) && (($maxlevel>$level+1) || ($maxlevel==0)))
{
$this->makeBranch($item[$this->primary_field], $level+1, $maxlevel);
}
}
}
}
function chainCMP($a,$b)
{
if($a[$a['key']] == $b[$b['key']])
{
return 0;
}
return($a[$a['key']]<$b[$b['key']])?-1:1;
}
?>


g8z

<?php
/**
This sort function allows you to sort an associative array while "sticking" some fields.
$sticky_fields = an array of fields that should not be re-sorted. This is a method of achieving sub-sorts within contiguous groups of records that have common data in some fields.
For example:
$a = array();
$a []= array(
'name' => 'Sam',
'age' => 23,
'hire_date' => '2004-01-01'
);
$a []= array(
'name' => 'Sam',
'age' => 44,
'hire_date' => '2003-03-23'
);
$a []= array(
'name' => 'Jenny',
'age' => 20,
'hire_date' => '2000-12-31'
);
$a []= array(
'name' => 'Samantha',
'age' => 50,
'hire_date' => '2000-12-14'
);
$sticky_fields = array( 'name' );
print_r( stickysort( $a, 'age', DESC_NUM, $sticky_fields ) );
OUTPUT:
Array
(
   [0] => Array
       (
           [name] => Sam
           [age] => 44
           [hire_date] => 2003-03-23
       )
   [1] => Array
       (
           [name] => Sam
           [age] => 23
           [hire_date] => 2004-01-01
       )
   [2] => Array
       (
           [name] => Jenny
           [age] => 20
           [hire_date] => 2000-12-31
       )
   [3] => Array
       (
           [name] => Samantha
           [age] => 50
           [hire_date] => 2000-12-14
       )
)
Here's why this is the correct output - the "name" field is sticky, so it cannot change its sort order. Thus, the "age" field is only sorted as a sub-sort within records where "name" is identical. Thus, the "Sam" records are reversed, because 44 > 23, but Samantha remains at the bottom, even though her age is 50. This is a way of achieving "sub-sorts" and "sub-sub-sorts" (and so on) within records of identical data for specific fields.
Courtesy of the $5 Script Archive: http://www.tufat.com
**/
define( 'ASC_AZ', 1000 );
define( 'DESC_AZ', 1001 );
define( 'ASC_NUM', 1002 );
define( 'DESC_NUM', 1003 );
function stickysort( $arr, $field, $sort_type, $sticky_fields = array() ) {
$i = 0;
foreach ($arr as $value) {
$is_contiguous = true;
if(!empty($grouped_arr)) {
$last_value = end($grouped_arr[$i]);
if(!($sticky_fields == array())) {
foreach ($sticky_fields as $sticky_field) {
if ($value[$sticky_field] <> $last_value[$sticky_field]) {
$is_contiguous = false;
break;
}
}
}
}
if ($is_contiguous)
$grouped_arr[$i][] = $value;
else
$grouped_arr[++$i][] = $value;
}
$code = '';
switch($sort_type) {
case ASC_AZ:
$code .= 'return strcasecmp($a["'.$field.'"], $b["'.$field.'"]);';
break;
case DESC_AZ:
$code .= 'return (-1*strcasecmp($a["'.$field.'"], $b["'.$field.'"]));';
break;
case ASC_NUM:
$code .= 'return ($a["'.$field.'"] - $b["'.$field.'"]);';
break;
case DESC_NUM:
$code .= 'return ($b["'.$field.'"] - $a["'.$field.'"]);';
break;
}
$compare = create_function('$a, $b', $code);
foreach($grouped_arr as $grouped_arr_key=>$grouped_arr_value)
usort ( $grouped_arr[$grouped_arr_key], $compare );
$arr = array();
foreach($grouped_arr as $grouped_arr_key=>$grouped_arr_value)
foreach($grouped_arr[$grouped_arr_key] as $grouped_arr_arr_key=>$grouped_arr_arr_value)
$arr[] = $grouped_arr[$grouped_arr_key][$grouped_arr_arr_key];
return $arr;
}
?>


ulrichunderscorealexis

<?php
/* returns the collated string (with no accent or with the matching string
* replacement) in lower case according to that language rule.
* The aim is to alphabetically sort lists of strings with characters from
* other languages but using collation rules of that given language
* (ISO 639-1 code).
* Latin-derived alphabets are supported (currently English, French and
* German)
* rules source: http://en.wikipedia.org/wiki/Latin_alphabet
*/
function collatedLower($string, $lang=null) {
switch ($lang) {
case 'de':
// German
/*
* In German, letters with umlaut (Ä, Ö, Ü) are treated generally
* just like their non-umlauted versions; ß is always sorted as ss.
* This makes the alphabetic order Arg, Ärgerlich, Arm, Assistent,
* Aßlar, Assoziation.
* For phone directories and similar lists of names, the umlauts are
* to be collated like the letter combinations "ae", "oe", "ue".
* This makes the alphabetic order Udet, Übelacker, Uell, Ülle, Ueve,
* Üxküll, Uffenbach.
* The ligatures Æ, Œ, and the symbol ß, when used in English, French,
* or German, are normally not counted as separate alphabetic letters
* but as variants of AE, OE, and ss, respectively.
*/
$patterns = array("/à|á|â|ã|å|À|Á|Â|Ã|Å/",
"/ä|æ|Ä|Æ/",
"/ç|Ç/",
"/é|è|ê|ë|É|È|Ê|Ë/",
"/ì|í|î|ï|Ì|Í|Î|Ï/",
"/ñ|Ñ/",
"/ò|ó|ô|õ|ø|Ò|Ó|Ô|Õ|Ø/",
"/ö|œ|Ö|Œ/",
"/ß/",
"/ù|ú|û|Ù|Ú|Û/",
"/ü|Ü/",
"/ý|ÿ|Ý/");
$replace = array('a', 'ae', 'c', 'e', 'i', 'n', 'o', 'oe', 'ss', 'u', 'ue', 'y');
break;
default:
// 'en' (English), 'fr' (French)
/*
* In French and English, characters with diaeresis (ä, ë, ï, ö, ü, ÿ)
* are usually treated just like their un-accented versions.
* The ligatures Æ, Œ, and the symbol ß, when used in English, French,
* or German, are normally not counted as separate alphabetic letters
* but as variants of AE, OE, and ss, respectively.
*
* NB: In French, accent differences are sorted from the end of the
* word, so the ordering of "pêche" and "péché" changes from the
* English ordering.
* English ordering: cote, coté, côte, côté
* French ordering: cote, côte, coté, côté
* => this is currently not supported
*/
$patterns = array("/à|á|â|ã|ä|å|À|Á|Â|Ã|Ä|Å/",
"/æ|Æ/",
"/ç|Ç/",
"/é|è|ê|ë|É|È|Ê|Ë/",
"/ì|í|î|ï|Ì|Í|Î|Ï/",
"/ñ|Ñ/",
"/ò|ó|ô|õ|ö|ø|Ò|Ó|Ô|Õ|Ö|Ø/",
"/œ|Œ/",
"/ß/",
"/ù|ú|û|ü|Ù|Ú|Û|Ü/",
"/ý|ÿ|Ý/");
$replace = array('a', 'ae', 'c', 'e', 'i', 'n', 'o', 'oe', 'ss', 'u', 'y');
break;
  }
return preg_replace($patterns,$replace,$string);
}
// English
function compareAccents_en($a, $b) {
return compareAccents($a, $b, 'en');
}
// French
function compareAccents_fr($a, $b) {
return compareAccents($a, $b, 'fr');
}
// German
function compareAccents_de($a, $b) {
return compareAccents($a, $b, 'de');
}
/*
* comparison function to group together accented letters with their lower-case
* non-accented counterparts
* the $lang parameter enables sorting rules for that language (ISO 639-1 code)
*/
function compareAccents($a, $b, $lang=null) {
$anew = strtolower(collatedLower($a,$lang));
$bnew = strtolower(collatedLower($b,$lang));
if ($anew < $bnew) return -1;
if ($anew > $bnew) return 1;
return 0;
}
// usage:
usort($myList,'compareAccents_fr');
// to be compared to:
setlocale(LC_COLLATE,'fr');
usort($myList, 'strcoll');
?>


Change Language


Follow Navioo On Twitter
array_change_key_case
array_chunk
array_combine
array_count_values
array_diff_assoc
array_diff_key
array_diff_uassoc
array_diff_ukey
array_diff
array_fill_keys
array_fill
array_filter
array_flip
array_intersect_assoc
array_intersect_key
array_intersect_uassoc
array_intersect_ukey
array_intersect
array_key_exists
array_keys
array_map
array_merge_recursive
array_merge
array_multisort
array_pad
array_pop
array_product
array_push
array_rand
array_reduce
array_reverse
array_search
array_shift
array_slice
array_splice
array_sum
array_udiff_assoc
array_udiff_uassoc
array_udiff
array_uintersect_assoc
array_uintersect_uassoc
array_uintersect
array_unique
array_unshift
array_values
array_walk_recursive
array_walk
array
arsort
asort
compact
count
current
each
end
extract
in_array
key
krsort
ksort
list
natcasesort
natsort
next
pos
prev
range
reset
rsort
shuffle
sizeof
sort
uasort
uksort
usort
eXTReMe Tracker