Here's how you can usort() a multidimensional array (like in example #2 above) within a class:
usort($myArray, array("className", "cmp"));
usort
(PHP 4, PHP 5)
usort — Sort an array by values using a user-defined comparison function
Description
This function will sort an array by its values using a user-supplied comparison function. If the array you wish to sort needs to be sorted by some non-trivial criteria, you should use this function.
Note: If two members compare as equal, their order in the sorted array is undefined.
Note: This function assigns new keys to the elements in array . It will remove any existing keys that may have been assigned, rather than just reordering the keys.
Parameters
- array
-
The input array.
- cmp_function
-
The comparison function must return an integer less than, equal to, or greater than zero if the first argument is considered to be respectively less than, equal to, or greater than the second.
Return Values
Returns TRUE on success or FALSE on failure.
ChangeLog
| Version | Description |
|---|---|
| 4.1.0 | A new sort algorithm was introduced. The cmp_function doesn't keep the original order for elements comparing as equal. |
Examples
Example #1 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
Note: Obviously in this trivial case the sort() function would be more appropriate.
Example #2 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 #3 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: */
static 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
usort
22-Jun-2008 10:16
13-May-2008 10:50
Here's how to use usort() within a class using a static function as a callback (using the example above):
<?php
class MyObj {
var $name;
function SampleObj($name){
$this->name = $name;
usort($this->name, array(&$this, "compareValues"));
}
static function compareValues($a, $b){
$al = strtolower($a->name);
$bl = strtolower($b->name);
if ($al == $bl) {
return 0;
}
return ($al > $bl) ? +1 : -1;
}
}
?>
14-Mar-2008 05:37
From the docs the user defined function looks as it could be arbitrary. This is not the case.
I needed a comparison that would send arbitrary values at the end of the array. Say '0' means invalid for example.
So here is a sample function.
function array_cmp($a, $b) {
if( $a == 0 ) {
return 99;
} elseif( $a > $b ) {
return 1;
} elseif( $a == $b ) {
return 0;
} elseif( $a < $b ) {
return -1;
}
}
usort($test_array, "array_cmp");
print '<pre>'; print_r( $test_array ); print '</pre>';
$test_array = array(
'a' => 8 ,
'b' => 0 ,
'c' => 1 ,
'd' => 0 ,
'e' => 3
);
Output:
Array
(
[0] => 0
[1] => 1
[2] => 8
[3] => 0
[4] => 3
)
This is beacause comparisons aren't applied to all possible pairs. So when the '0' is tested as $b strange things can occur...
This is solved simply adding an
} elseif( $b == 0) {
return -99;
}
statement.
Carefull of the pitfalls. If you alter the ordering in strange ways without knowledge in the underlying algorithm one could even trigger infinite loops. (if your testing for $a and $b for the same value give inconsistent results. )
05-Nov-2007 05:54
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.
09-Oct-2007 06:20
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);
?>
28-Apr-2007 04:09
<?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'));
?>
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.
30-Jan-2007 07:44
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);
13-Nov-2006 03:43
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
16-Oct-2006 01: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>';
?>
21-Sep-2006 03: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.
06-Jul-2006 03: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;
}
?>
16-May-2006 06: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;
}
16-May-2006 01: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);
?>
05-Apr-2006 10: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.
16-Jan-2006 01: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.
23-Sep-2005 02: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
22-Sep-2005 07: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.
15-Sep-2005 12: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');
?>
15-Sep-2005 11: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 = "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;
?>
10-Sep-2005 09:33
reinhard at ess dot co dot at: you need to look at the natsort() function.
03-Sep-2005 12: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.
08-Aug-2005 06: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");
?>
20-Jul-2005 01: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;
}
?>
19-Jul-2005 02: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.
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
)
*/
?>
12-Apr-2005 04: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.
30-Mar-2005 02: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...
04-Mar-2005 07: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(
"/á|à|â|ã|å|
Á|À|Â|Ã|Å/",
"/ä|æ|Ä|Æ/",
"/ç|Ç/",
"/é|è|ê|ë|
É|È|Ê|Ë/",
"/ì|í|î|ï|
Ì|Í|Î|Ï/",
"/ñ|Ñ/",
"/ò|ó|ô|õ|ø|
Ò|Ó|Ô|Õ|Ø/",
"/ö|œ|Ö|Œ/",
"/ß/",
"/ù|ú|û|
Ù|Ú|Û/",
"/ü|Ü/",
"/ý|ÿ|Ý/");
$replace = array('a', 'ae', 'c', 'e', 'i', 'n', 'o', 'oe', 'ss', 'u', 'ue', 'y');
break;
default:
// 'en' (English), 'fr' (French)
$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');
?>
03-Mar-2005 04: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,
* 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');
?>
20-May-2004 10: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;
}
?>
10-Mar-2004 01: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)))
{
