|
|
 |
usort (PHP 3 >= 3.0.3, PHP 4, PHP 5) usort --
Sort an array by values using a user-defined comparison function
Descriptionbool usort ( array &array, callback cmp_function )
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.
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.
Note:
If two members compare as equal, their order in the sorted array is undefined.
Up to PHP 4.0.6 the user defined functions would keep the original order for
those elements, but with the new sort algorithm introduced with 4.1.0 this
is no longer the case as there is no solution to do so in an efficient way.
Returns TRUE on success or FALSE on failure.
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");
while (list($key, $value) = each($a)) {
echo "$key: $value\n";
}
?>
|
The above example will output: |
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;
}
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: |
See also uasort(),
uksort(), sort(),
asort(),
arsort(),ksort(),
natsort(), and rsort().
User Contributed Notes
usort
steve at tracorp dot com
12-Apr-2005 06: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 04: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 09:03
Please note that:
- the HTML entities should be replaced by their accented counterparts;
- the $patterns arrays have been split for display convenience.
<?php
function collatedLower($string, $lang=null) {
switch ($lang) {
case 'de':
$patterns = array(
"/á|à|â|ã|å|
Á|À|Â|Ã|Å/",
"/ä|æ|Ä|Æ/",
"/ç|Ç/",
"/é|è|ê|ë|
É|È|Ê|Ë/",
"/ì|í|î|ï|
Ì|Í|Î|Ï/",
"/ñ|Ñ/",
"/ò|ó|ô|õ|ø|
Ò|Ó|Ô|Õ|Ø/",
"/ö|œ|Ö|Œ/",
"/ß/",
"/ù|ú|û|
Ù|Ú|Û/",
"/ü|Ü/",
"/ý|ÿ|Ý/");
$replace = array('a', 'ae', 'c', 'e', 'i', 'n', 'o', 'oe', 'ss', 'u', 'ue', 'y');
break;
default:
$patterns = array(
"/á|à|â|ã|ä|å|
Á|À|Â|Ã|Ä|Å/",
"/æ|Æ/",
"/ç|Ç/",
"/é|è|ê|ë|
É|È|Ê|Ë/",
"/ì|í|î|ï|
Ì|Í|Î|Ï/",
"/ñ|Ñ/",
"/ò|ó|ô|õ|ö|ø|
Ò|Ó|Ô|Õ|Ö|Ø/",
"/œ|Œ/",
"/ß/",
"/ù|ú|û|ü|
Ù|Ú|Û|Ü/",
"/ý|ÿ|Ý/");
$replace = array('a', 'ae', 'c', 'e', 'i', 'n', 'o', 'oe', 'ss', 'u', 'y');
break;
}
return preg_replace($patterns,$replace,$string);
}
function compareAccents_en($a, $b) {
return compareAccents($a, $b, 'en');
}
function compareAccents_fr($a, $b) {
return compareAccents($a, $b, 'fr');
}
function compareAccents_de($a, $b) {
return compareAccents($a, $b, 'de');
}
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;
}
usort($myList,'compareAccents_fr');
setlocale(LC_COLLATE,'fr');
usort($myList, 'strcoll');
?>
ulrichUNDERSCOREalexis at yahoo dot com
03-Mar-2005 06:31
<?php
function collatedLower($string, $lang=null) {
switch ($lang) {
case 'de':
$patterns = array("/à|á|â|ã|å|À|Á|Â|Ã|Å/",
"/ä|æ|Ä|Æ/",
"/ç|Ç/",
"/é|è|ê|ë|É|È|Ê|Ë/",
"/ì|í|î|ï|Ì|Í|Î|Ï/",
"/ñ|Ñ/",
"/ò|ó|ô|õ|ø|Ò|Ó|Ô|Õ|Ø/",
"/ö|œ|Ö|Œ/",
"/ß/",
"/ù|ú|û|Ù|Ú|Û/",
"/ü|Ü/",
"/ý|ÿ|Ý/");
$replace = array('a', 'ae', 'c', 'e', 'i', 'n', 'o', 'oe', 'ss', 'u', 'ue', 'y');
break;
default:
$patterns = array("/à|á|â|ã|ä|å|À|Á|Â|Ã|Ä|Å/",
"/æ|Æ/",
"/ç|Ç/",
"/é|è|ê|ë|É|È|Ê|Ë/",
"/ì|í|î|ï|Ì|Í|Î|Ï/",
"/ñ|Ñ/",
"/ò|ó|ô|õ|ö|ø|Ò|Ó|Ô|Õ|Ö|Ø/",
"/œ|Œ/",
"/ß/",
"/ù|ú|û|ü|Ù|Ú|Û|Ü/",
"/ý|ÿ|Ý/");
$replace = array('a', 'ae', 'c', 'e', 'i', 'n', 'o', 'oe', 'ss', 'u', 'y');
break;
}
return preg_replace($patterns,$replace,$string);
}
function compareAccents_en($a, $b) {
return compareAccents($a, $b, 'en');
}
function compareAccents_fr($a, $b) {
return compareAccents($a, $b, 'fr');
}
function compareAccents_de($a, $b) {
return compareAccents($a, $b, 'de');
}
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;
}
usort($myList,'compareAccents_fr');
setlocale(LC_COLLATE,'fr');
usort($myList, 'strcoll');
?>
gk at lka dot hu
16-Aug-2004 07: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 = "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 )".)
Raveler at telenet dot be
20-May-2004 12: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
10-Mar-2004 03: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 06: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 12:05
<?php
$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'].'<br>';
}
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;
}
?>
sreid at sea-to-sky dot net
07-Jan-2004 10:22
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;
}
webmaster at zeroweb dot org
20-Dec-2003 05:05
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);
}
kristen at ccshoreline dot org
22-Nov-2003 02:39
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);
me_islandnet_com
08-Oct-2003 08:45
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;var $aSortkeys;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 '<br>';
}
?>
skrebbel at operamail dot com
27-Mar-2003 03:59
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.
steveNO_SPAM at AXE_MEcontentwatch dot com
31-Dec-2002 04:11
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 ) {
// ... //
}
}
mharrodine AT blue yonder DOT co-uk
11-Dec-2002 06:46
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....
jfren484 AT hotmail DOT com
24-Oct-2002 06:37
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')));
mkr at binarywerks dot dk
20-Sep-2002 11:29
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);
simen at NO_SPAM_AT_ALLbleed dot no
06-Aug-2002 03:16
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;
}
}
prozac at iguanasoft dot com
15-Jul-2002 07:55
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
alex at netflex dot nl
02-Jul-2002 06:10
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>
graham at irwin dot org
15-Jan-2002 10:28
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.
jonathan at inetz dot com
14-Nov-2001 02:48
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 )
{
$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.
xnoguer at rezebra dot com
25-Sep-2001 01:48
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...
josh at wintelcom dot net
24-Aug-2001 08:58
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;
}
}
}
}
franky at iname dot com
17-Jul-2001 05:08
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");
johan_land at yahoo dot com
29-Jun-2001 07:23
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);
06-Apr-2001 06: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.
bo at erichsen dot com
20-Mar-2001 07:16
when using usort to refer to a function inside a class i have succesfully used:
usort($myarray,array($this,"cmp"));
derek at luddite dot net
19-Sep-2000 01:35
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
gfaron at integretechpub dot com
28-Feb-2000 02:56
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);
$temp = $array[$n];
$array[$n] = $array[$i];
$array[$i] = $temp;
} } $array = array(1,2,3,4,5,6,7,8,9,10);
myshuffle(&$array);
while (list(,$var)=each($array))
echo $var . " ";
?>
| |