usort

(PHP 3 >= 3.0.3, PHP 4, PHP 5)

usort --  使用用户自定义的比较函数对数组中的值进行排序

说明

bool usort ( array &array, callback cmp_function )

本函数将用用户自定义的比较函数对一个数组中的值进行排序。如果要排序的数组需要用一种不寻常的标准进行排序,那么应该使用此函数。

比较函数必须在第一个参数被认为小于,等于或大于第二个参数时分别返回一个小于,等于或大于零的整数。

注: 如果两个成员比较结果相同,则它们在排序后的数组中的顺序未经定义。到 PHP 4.0.6 之前,用户自定义函数将保留这些单元的原有顺序。但是由于在 4.1.0 中引进了新的排序算法,结果将不是这样了,因为对此没有一个有效的解决方案。

注: 本函数为 array 中的单元赋予新的键名。这将删除原有的键名而不仅是重新排序。

如果成功则返回 TRUE,失败则返回 FALSE

例子 1. usort() 例子

<?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";
}
?>

上例将输出:

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

注: 很明显在这个小例子中用 sort() 函数更合适。

例子 2. 使用多维数组的 usort() 例子

<?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";
}
?>

当排序多维数组时,$a$b 包含到数组第一个索引的引用。

上例将输出:

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

例子 3. 使用对象的成员函数的 usort() 例子

<?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";
}
?>

上例将输出:

b
c
d

参见 uasort()uksort()sort()asort()arsort()ksort()natsort()rsort()


add a note add a note User Contributed Notes
svenxy AT nospam gmx net
16-Oct-2006 04:58
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>';

?>
oscahie at gmx dot net
21-Sep-2006 06:51
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.
g8z at yahoo dot com
07-Jul-2006 06:53
<?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;
}
?>
Number type ???
17-May-2006 09:13
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;
}
martin # classaxe com <><
17-May-2006 04:07
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);
?>
stephen in koto
06-Apr-2006 01:28
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.
sydney at totoche dot org
16-Jan-2006 05:44
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 at NOSPAM dot orwd dot com
24-Sep-2005 05:46
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
Tim
22-Sep-2005 10:25
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 at udo dot edu
16-Sep-2005 03:00
@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');

?>
markus dot meier at udo dot edu
16-Sep-2005 02:53
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 = "AaBbCcDdEeFfGgHhIiiJjKkLlMmNnOoPpQqRrSs"
              
"TtUuVvWwXxYyZz0123456789!?.()[]=%+-"

      
// 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;
?>
jasper at bryant-greene dot name
11-Sep-2005 12:33
reinhard at ess dot co dot at: you need to look at the natsort() function.
rpmPUBLI at iespana dot es
04-Sep-2005 03:48
(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.
reinhard at ess dot co dot at
08-Aug-2005 09:34
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");
?>
Jeremy Swinborne
21-Jul-2005 04:56
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;
}
?>
dmhouse at gmail dot com
19-Jul-2005 05:09
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.
16-Jun-2005 05: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
)
*/
?>
steve at tracorp dot com
13-Apr-2005 07:57
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.
guenther dot stadler at gmx dot net
30-Mar-2005 06:25
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...
ulrichUNDERSCOREalexis at yahoo dot com
04-Mar-2005 11:03
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');

?>
ulrichUNDERSCOREalexis at yahoo dot com
04-Mar-2005 08:31
<?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,
             * Alar, 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,
             * xkll, 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 "pche" and "pch" changes from the
             * English ordering.
             * English ordering: cote, cot, cte, ct
             * French ordering: cote, cte, cot, ct
             * => 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');

?>
gk at lka dot hu
17-Aug-2004 08:28
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 = "AaBbCcDdEeFfGgHhIiiJjKkLlMmNnOoPpQqRr".
  "SsTtUuVvWwXxYyZz0123456789!?.()[]=%+-";

  //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","bla","gnes","GNES","zazie","rdekes");
$ize = sort_hun($ize);

(It returns "Array ( [0] => GNES [1] => gnes [2] => bla [3] => rdekes [4] => jani [5] => zazie )".)
Raveler at telenet dot be
21-May-2004 01:02
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;
   }
?>
robert at sargant dot com
11-Mar-2004 05:40
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
       )

)
todor at todor dot net
08-Mar-2004 08:39
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 ;
}
arjini at mac dot com
27-Jan-2004 02:05
<?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(