PHP Doku:: Sortiert ein Array nach Werten mittels einer benutzerdefinierten Vergleichsfunktion - function.usort.html

Verlauf / Chronik / History: (1) anzeigen

Sie sind hier:
Doku-StartseitePHP-HandbuchFunktionsreferenzVariablen- und typbezogene ErweiterungenArraysArray Funktionenusort

Ein Service von Reinhard Neidl - Webprogrammierung.

Array Funktionen

<<uksort

Klassen- und Objektinformationen>>

usort

(PHP 4, PHP 5)

usortSortiert ein Array nach Werten mittels einer benutzerdefinierten Vergleichsfunktion

Beschreibung

bool usort ( array &$array , callback $cmp_function )

Diese Funktion sortiert ein Array nach seinen Werten mittels einer benutzerdefinierten Vergleichsfunktion. Soll das Array nach nicht trivialen Kriterien sortiert werden, sollten Sie diese Funktion verwenden.

Hinweis:

Wenn zwei Elemente als gleich verglichen werden, ist ihre Reihenfolge im sortierten Array undefiniert.

Hinweis: Diese Funktion weist den Elementen des Arrays array neue Schlüssel zu. Bestehende Schlüssel, die Sie eventuell zugewiesen haben, werden entfernt statt einfach nur die Schlüssel neu anzuordnen

Parameter-Liste

array

Das zu sortierende Array.

cmp_function

Die Vergleichsfunktion muss einen Integerwert kleiner als, gleich oder größer als 0 zurückgeben je nachdem ob das erste übergebene Argument kleiner als, gleich oder größer als das zweite ist.

Rückgabewerte

Gibt bei Erfolg TRUE zurück. Im Fehlerfall wird FALSE zurückgegeben.

Changelog

Version Beschreibung
4.1.0 Es wurde ein neuer Sotieralgorithmus eingeführt, die Reihenfolge von Elementen die von cmp_function als gleich bewertet wurden bleibt nun nicht mehr erhalten.

Beispiele

Beispiel #1 usort()-Beispiel

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

$a = array(32561);

usort($a"cmp");

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

Das oben gezeigte Beispiel erzeugt folgende Ausgabe:

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

Hinweis:

Für dieses einfache Beispiel wäre die sort()-Funktion offensichtlich die bessere Wahl.

Beispiel #2 usort()-Beispiel für multidimensionale Arrays

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

$fruechte[0]["frucht"] = "Zitronen";
$fruechte[1]["frucht"] = "Aepfel";
$fruechte[2]["frucht"] = "Orangen";

usort($fruechte"cmp");

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

Wenn ein mehrdimensionales Array sortiert wird, enthalten $a und $b Verweise auf den ersten Index des Arrays.

Das oben gezeigte Beispiel erzeugt folgende Ausgabe:

$fruechte[0]: Aepfel
$fruechte[1]: Orangen
$fruechte[2]: Zitronen

Beispiel #3 usort()-Beispiel, das die Methode eines Objekts benutzt

<?php
class TestObj {
    var 
$name;

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

    
/* Dies ist die statische Vergleichsfunktion */
    
static function cmp_obj($a$b)
    {
        
$al strtolower($a->name);
        
$bl strtolower($b->name);
        if (
$al == $bl) {
            return 
0;
        }
        return (
$al $bl) ? +: -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";
}
?>

Das oben gezeigte Beispiel erzeugt folgende Ausgabe:

b
c
d

Siehe auch


74 BenutzerBeiträge:
- Beiträge aktualisieren...
fmaz008 at gmail dot com
29.09.2010 23:23
This comment is just to help backward compatibility.

I needed to have a foreach loop using reference just before using the usort function on the same array that was used in the foreach.

Like this:
<?php
public function sortByTitle()
{
        if(empty(
$this->_arrSubCat))
            return
false;
       
        foreach(
$this->_arrSubCat as &$cat)
           
$cat->sortByTitle();
       
       
usort($this->_arrSubCat, 'strcasecmp');
}
?>

I had this strange bug where the last object of the $this->_arrSubCat array before the sort was alway replaced/overwrited with his string value.

On my machine (PHP 5.3), I had no problem, but when I placed the code on another server, my code crashed with a fatal error (Because I was using a string as an object later in the code)

The solution I found is really simple, and avoid the array to have a reference declared in memory while running the sort function. Just add an unset($cat):

<?php
public function sortByTitle()
{
        if(empty(
$this->_arrSubCat))
            return
false;
       
        foreach(
$this->_arrSubCat as &$cat)
           
$cat->sortByTitle();
       
        unset(
$cat); //No more problem because of the foreach &cat reference.   
       
usort($this->_arrSubCat, 'strcasecmp');
}
?>

It took me an hour an a half to locate the bug, hope it will save someone some time !
phil at lavin dot me dot uk
1.08.2010 20:06
strcmp (or strcasecmp) can be used as a callback function for usort. It will cast your comparison values to string. Thus, you can implement a __toString() method into your classes and use usort without creating a callback function:

<?php
class Foo {
   private
$a;
   private
$b;

   public function
__construct($a, $b) {
     
$this->a = $a;
     
$this->b = $b;
   }

   public function
__toString() {
      return (string)
$this->a;
   }
}

$foo = array(new Foo('t', 'b'), new Foo('a', 'c'), new Foo('g', 'd'));

print_r($foo);

usort($foo, 'strcasecmp');

print_r($foo);
?>
xaguilars at gmail dot com
6.05.2010 13:03
I'd like to share with the community my function for sorting an array of arrays or objects containing associative data. This could be used, for example, with a MySQL result.

<?php
function sort_by($field, &$arr, $sorting=SORT_ASC, $case_insensitive=true){
    if(
is_array($arr) && (count($arr)>0) && ( ( is_array($arr[0]) && isset($arr[0][$field]) ) || ( is_object($arr[0]) && isset($arr[0]->$field) ) ) ){
        if(
$case_insensitive==true) $strcmp_fn = "strnatcasecmp";
        else
$strcmp_fn = "strnatcmp";

        if(
$sorting==SORT_ASC){
           
$fn = create_function('$a,$b', '
                if(is_object($a) && is_object($b)){
                    return '
.$strcmp_fn.'($a->'.$field.', $b->'.$field.');
                }else if(is_array($a) && is_array($b)){
                    return '
.$strcmp_fn.'($a["'.$field.'"], $b["'.$field.'"]);
                }else return 0;
            '
);
        }else{
           
$fn = create_function('$a,$b', '
                if(is_object($a) && is_object($b)){
                    return '
.$strcmp_fn.'($b->'.$field.', $a->'.$field.');
                }else if(is_array($a) && is_array($b)){
                    return '
.$strcmp_fn.'($b["'.$field.'"], $a["'.$field.'"]);
                }else return 0;
            '
);
        }
       
usort($arr, $fn);
        return
true;
    }else{
        return
false;
    }
}
?>
Alex Rath
17.04.2010 20:35
This is the perfect Place for anonymous Functions, here an Example:

<?php

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

 
usort($array, function($func_a, $func_b) {
    if(
$func_a == $func_b) return 0;
    return (
$func_a < $func_b) ? -1 : 1;
  });

  foreach (
$array as $key => $value) echo "$key: $value\n";

?>
nicolas dot demarque at gmail dot com
10.04.2010 20:37
Another way to compare object is to have a compare static function in the class with reccursion ability and have globals or static variables who save the state of comparison, and the order.

It could be a good idea to have a variable with comparison functions name (with the sorting : numeric, alphanumeric, inverse).

<?php
interface Comparable{static function compare($a, $b);}
class
myObj implements Comparable{
        var
$var1; var $var2; var $var3;
        function
__construct($v1, $v2, $v3){
               
$this->var1=$v1; $this->var2=$v2; $this->var3=$v3;
        }
        public function
__toString() {
                return
"v1 ".$this->var1." v2 ".$this->var2." v3 ".$this->var3;
        }
        static function
compare($a, $b){
               
$sort=$GLOBALS['sorts'][$GLOBALS['sort']];
                echo
"$sort\n";
                if(
$a->$sort == $b->$sort){
                        if(
$GLOBALS['sorts'][$GLOBALS['sort']+1]){
                               
$GLOBALS['sort']++;
                               
$rescall_user_func(array('myObj', 'compare'), $a, $b);
                               
$GLOBALS['sort']=0; return $res;
                        }
                       
$GLOBALS['sort']=0; return 0;
                }
               
$GLOBALS['sort']=0;
                if(
$a->$sort > $b->$sort)
                        return
1;
                return -
1;
        }
}

$objects = array(new myObj("1", "2", "3"), new myObj("2", "2", "2"), new myObj("1", "3", "4"));

/* Tests */
$sorts=Array('var1', 'var2', 'var3'); $sort = 0; usort($objects, array('myObj', 'compare')); echo ("var1 var2 var3\n"); foreach($objects as $o)echo $o."\n";
$sorts=Array('var2', 'var3', 'var1'); $sort = 0; usort($objects, array('myObj', 'compare')); echo ("\nvar2 var3 var1\n"); foreach($objects as $o)echo $o."\n";
$sorts=Array('var2', 'var1', 'var3'); $sort = 0; usort($objects, array('myObj', 'compare')); echo ("\nvar2 var1 var3\n"); foreach($objects as $o)echo $o."\n";

?>
nicolas dot demarque at gmail dot com
7.03.2010 20:02
usort function has problem,if you use more than one time :  sorts used before are lost.

So i have write this function which functions ;)

A problem rest, this function spend 30% more processor's time.

<?php
function usort(&$tableau, $fonction){
        while(
count($tableau)>0){
           
reset($tableau);
           
$pluspetit = key($tableau);
            foreach(
$tableau as $c=>$v){
                if(
call_user_func($fonction, $tableau[$pluspetit], $v)>0){
                   
$pluspetit = $c;
                }
            }
           
$result[]=$tableau[$pluspetit];
            unset(
$tableau[$pluspetit]);
        }
       
$tableau = $result;   
    }
}
?>
Paul Geraghty foofoonet at the G Mall
27.11.2009 20:42
For anyone else sorting mixed RSS feeds in date order, here's an example of the perhaps not so obvious way of calling a method from the same class using usort():

<?php
class MyAggregator {

  static function
cmp_pubdate( $a, $b ) {
   
$a_t = strtotime( $a['pubdate'] ) ;
   
$b_t = strtotime( $b['pubdate'] ) ;

    if(
$a_t == $b_t ) return 0 ;
    return (
$a_t > $b_t ) ? -1 : 1;
  }

// assemble arrays for display in date order
 
function all_documents(){
   
$all_data  = array_merge (
   
$this->upcoming(), $this->past() ) ;

// Use within its own class using the $this syntax so:
   
usort( $all_data, array( $this , "cmp_pubdate" ) ) ;

  return
$all_data ;
  }
}
?>

As previously said, use as a static function in other parts of your code:

<?php usort ( $a, array( "MyAggregator", "cmp_pubdate" ) ) ; ?>
Rogier
16.11.2009 12:50
<?php
// I have seen many examples of sorting, almost all of which
// are too specific and require one implementation per property
// on which to sort.
// So I decided to use Reflection, implementing a more generic approach that in fact turned out
// to resemble an approach I used in the early days of .NET...

// Intentionally, I did not do any checking on existing
// items. This is just to keep simplicity and to
// demonstrate the sorting functionality.

// A more real-life appraoach would require e.g. a collection that is based
// on implementing the Iterator, ArrayAccess and Countable interfaces.

// Take note that this implementation is specific for PHP5+ only
// This code was tested on PHP 5.0.4

class SortableItem
{
    static public
$sortProperty;
   
    static function
USortAsc($a, $b)
    {
       
/*@var $_pd ReflectionProperty*/
       
$_pd = self::$sortProperty;
        if (
$_pd !== null)
        {
            if (
$_pd->getValue($a) === $_pd->getValue($b))
                return
0;
            else
                return ((
$_pd->getValue($a) < $_pd->getValue($b))?-1:1);
        }
        return
0;
    }

    static function
USortDesc($a, $b)
    {
        return -(
self::USortAsc($a,$b));
    }

}
class
Item extends SortableItem
{
    public
$name;
    public
$index;
    public
$description;
   
    function
__construct($nam,$idx,$desc)
    {
       
$this->name = $nam;
       
$this->index = $idx;
       
$this->description = $desc;
    }
    function
__toString()
    {
        return
"$this->index; $this->name; $this->description\n";
    }
}
class
ItemCollection
{
    protected
$_array = array();
    protected
$_itemDescriptor;
   
    function
__construct()
    {
       
$this->_itemDescriptor = new ReflectionClass('Item');
    }
   
    function
Add(Item $item)
    {
       
$this->_array[] = $item;
    }
   
    function
Sort($property, $asc=true)
    {
       
// this is where sorting logic takes place
       
$_pd = $this->_itemDescriptor->getProperty($property);
        if (
$_pd == null)
        {
           
user_error('Property '.$property.' does not exist in class '.$this->_itemDescriptor->getName(), E_WARNING);
            return;
        }
       
// set sortDescriptor
       
Item::$sortProperty = $_pd;
       
// and apply sorting
       
usort($this->_array, array('Item', ($asc?'USortAsc':'USortDesc')));
    }
   
    function
getItems(){return $this->_array;}
}

//testing it....
$utime = microtime(true);
$coll = new ItemCollection();
$coll->Add( new Item('apple',0,'a sour fruit') );
$coll->Add( new Item('pear',54,'delicious pears') );
$coll->Add( new Item('mango',245,'bread and butter') );

echo
PHP_EOL,'sort by index, asc',PHP_EOL;
$coll->Sort('index',true);
$items = $coll->getItems();
foreach(
$items as $item)
{
    echo
$item->__toString();
}

echo
PHP_EOL,'sort by index, desc',PHP_EOL;
$coll->Sort('index',false);
$items = $coll->getItems();
foreach(
$items as $item)
{
    echo
$item->__toString();
}

echo
PHP_EOL,'sort by name, asc',PHP_EOL;
$coll->Sort('name',true);
$items = $coll->getItems();
foreach(
$items as $item)
{
    echo
$item->__toString();
}

echo
PHP_EOL,'sort by name, desc',PHP_EOL;
$coll->Sort('name',false);
$items = $coll->getItems();
foreach(
$items as $item)
{
    echo
$item->__toString();
}
$utime = microtime(true)-$utime;
echo
PHP_EOL,' sorting all took ',round(1000*$utime,2),' ms';
?>
Will Shaver
27.08.2009 23:31
An even better implementation of osort [than my original, posted on 24-AUG-09 (since deleted)], allowing for multiple properties and directions.  With php 5.3.0 sorting by properties of an object becomes MUCH simpler.  Note that this uses anonymous functions / closures. Might find reviewing the php docs on that useful.  Look below for examples for previous version of php.

<?php
function osort(&$array, $props)
{
    if(!
is_array($props))
       
$props = array($props => true);
       
   
usort($array, function($a, $b) use ($props) {
        foreach(
$props as $prop => $ascending)
        {
            if(
$a->$prop != $b->$prop)
            {
                if(
$ascending)
                    return
$a->$prop > $b->$prop ? 1 : -1;
                else
                    return
$b->$prop > $a->$prop ? 1 : -1;
            }
        }
        return -
1; //if all props equal       
   
});   
}
?>

Usage:

<?php
osort
($items, array("Color" => true, "Size" => false));
//or
osort($items, "Color");
?>
aj at ajcates dot com
26.08.2009 1:46
I rewrote Will Shaver's osort function to work with older versions of php.

<?php
function osort(&$oarray, $p) {
   
usort($oarray, create_function('$a,$b', 'if ($a->' . $p . '== $b->' . $p .') return 0; return ($a->' . $p . '< $b->' . $p .') ? -1 : 1;'));
}
?>
maarten at xel dot nl
17.07.2009 17:45
If you ever have to sort a collection of objects on something like a string (name) present in every object, but want to keep the array of objects intact you might find this useful:

<?php
function naturalSortObjectArray($objectArray) {
        function
naturalSortByName($a, $b) {
            return
strnatcmp($a->name, $b->name);
        }
       
usort($objectArray, "naturalSortByName");
        return
$objectArray;
}
?>
majkl578 at gmail dot com
26.06.2009 2:25
As of PHP 5.3 it is possible to use anonymous functions:

<?php
$arrayToSort
= array(2,6,1,9,3);

/* As of PHP 5.3 */
usort($arrayToSort, function($a, $b) {
    return
$a > $b ? 1 : -1;
});

var_dump($arrayToSort);

/* Returns:
array
  0 => int 1
  1 => int 2
  2 => int 3
  3 => int 6
  4 => int 9
*/
?>
phpguy at lifetoward dot com
12.06.2009 2:54
I recommend that you take special note of the fact that an INTEGER value must be returned from a sorting callback function. So, let's say you want to sort arrays by a quotient of two of their elements and to do so you write something like:
<?php
function sorter($a, $b)
{ return
$a['num']/$a['denom'] - $b['num']/$b['denom']; }
?>

You may not get the result you want because probably $x['num']/$x['denom'] has absolute value less than 1 and you'll get a lot of "same value" effective results from the comparator function as integer rounding occurs.

To solve this just boost up the quotients to be big integers, then subtract them:

<?php
function sorter($a, $b)
{ return
1000*$a['num']/$a['denom'] - 1000*$b['num']/$b['denom']; }
?>
huskyr at gmail dot com
28.05.2009 15:04
Remember that functions inside of functions will become part of the global scope too. So, something like:

<?php
   
function foo($array) {
        function
compare($a,$b) {
            return
$a < $b;
        }
       
usort($array, "compare");
        return
$array;
    }
   
    function
bar($array) {
        function
compare($a,$b) {
            return
$a > $b;
        }
       
usort($array, "compare");
        return
$array;
    }
   
   
foo($something);
   
bar($something_else);
?>
       
Will result in a fatal error because you're redeclaring 'compare'. This could be a little strange, e.g. for JavaScript programmers who expect functions in functions to become private outside of the parent scope.
gmail.com - nmcquay
31.03.2009 22:25
I wrote a wrapper for usort that lets you use something similar to an SQL ORDER BY clause. It can sort arrays of associative arrays and arrays of objects and I think it would work with some hybrid case.

Example of how the function works:

<?php
$testAry
= array(
  array(
'a' => 1, 'b' => 2, 'c' => 3),
  array(
'a' => 2, 'b' => 1, 'c' => 3),
  array(
'a' => 3, 'b' => 2, 'c' => 1),
  array(
'a' => 1, 'b' => 3, 'c' => 2),
  array(
'a' => 2, 'b' => 3, 'c' => 1),
  array(
'a' => 3, 'b' => 1, 'c' => 2)
);

Utility::orderBy($testAry, 'a ASC, b DESC');

//Result:
$testAry = array(
  array(
'a' => 1, 'b' => 3, 'c' => 2),
  array(
'a' => 1, 'b' => 2, 'c' => 3),
  array(
'a' => 2, 'b' => 3, 'c' => 1),
  array(
'a' => 2, 'b' => 1, 'c' => 3),
  array(
'a' => 3, 'b' => 2, 'c' => 1),
  array(
'a' => 3, 'b' => 1, 'c' => 2)
);
?>

To sort an array of objects you would do something like:
Utility::orderBy($objectAry, 'getCreationDate() DESC, getSubOrder() ASC');

This would sort an array of objects that have methods getCreationDate() and getSubOrder().

Here is the function:

<?php
class Utility {
   
/*
    * @param array $ary the array we want to sort
    * @param string $clause a string specifying how to sort the array similar to SQL ORDER BY clause
    * @param bool $ascending that default sorts fall back to when no direction is specified
    * @return null
    */
   
public static function orderBy(&$ary, $clause, $ascending = true) {
       
$clause = str_ireplace('order by', '', $clause);
       
$clause = preg_replace('/\s+/', ' ', $clause);
       
$keys = explode(',', $clause);
       
$dirMap = array('desc' => 1, 'asc' => -1);
       
$def = $ascending ? -1 : 1;

       
$keyAry = array();
       
$dirAry = array();
        foreach(
$keys as $key) {
           
$key = explode(' ', trim($key));
           
$keyAry[] = trim($key[0]);
            if(isset(
$key[1])) {
               
$dir = strtolower(trim($key[1]));
               
$dirAry[] = $dirMap[$dir] ? $dirMap[$dir] : $def;
            } else {
               
$dirAry[] = $def;
            }
        }

       
$fnBody = '';
        for(
$i = count($keyAry) - 1; $i >= 0; $i--) {
           
$k = $keyAry[$i];
           
$t = $dirAry[$i];
           
$f = -1 * $t;
           
$aStr = '$a[\''.$k.'\']';
           
$bStr = '$b[\''.$k.'\']';
            if(
strpos($k, '(') !== false) {
               
$aStr = '$a->'.$k;
               
$bStr = '$b->'.$k;
            }

            if(
$fnBody == '') {
               
$fnBody .= "if({$aStr} == {$bStr}) { return 0; }\n";
               
$fnBody .= "return ({$aStr} < {$bStr}) ? {$t} : {$f};\n";               
            } else {
               
$fnBody = "if({$aStr} == {$bStr}) {\n" . $fnBody;
               
$fnBody .= "}\n";
               
$fnBody .= "return ({$aStr} < {$bStr}) ? {$t} : {$f};\n";
            }
        }

        if(
$fnBody) {
           
$sortFn = create_function('$a,$b', $fnBody);
           
usort($ary, $sortFn);       
        }
    }
}
?>
luke dot semerau at gmail dot com
28.03.2009 21:25
If you need to use usort with a key in the calling method, I wrote this as a utility:
<?php

function usort_comparison($obj, $method, $key) {
   
$usorter = &new Usort($obj, $method, $key);
    return array(
$usorter, "sort");
}

class
Usort {
    function
__construct($obj, $method, $key) {
       
$this->obj = $obj;
       
$this->method = $method;
       
$this->key = $key;
    }
    function
sort($a, $b) {
        return
call_user_func_array(array($this->obj, $this->method), array($a, $b, $this->key));
    }
}

?>

<?php

require_once("util/usort.php");

class
Foo {
   
$items = array(FooBar(13), FooBar(2));
    public function
sorter() {
       
usort($this-items, usort_comparison("Foo", "_cmp", "item"));
    }

    public static function
_cmp($a, $b, $key) {
         return
strcasecmp($a->$key, $b->$key);
    }

}

class
FooBar {
    public
$item;
    function
__construct($val) {
       
$this->item = $val;
    }
}

?>

~ simple example... but in the way I need to use it was the key was used in a switch statement to choose the different member of the object to compare against dynamically (as in, sort by x or y or z)
fig dot miguel at gmail dot com
12.02.2009 13:29
I often need to order arrays by one dimension, therefore, I implemented a generic function, called "g_arr_dimsort":

<?
function arr_dimsort_cmp($a,$b)
/*
 *      Compare function used when sorting arrays.
 */
{
  global
$G_ARR_STYPE, $G_ARR_SDIM;

 
/* -- Sort numbers? */

 
if ($G_ARR_STYPE == 'NUMBER') {

      if ((float)
$a[$G_ARR_SDIM] == (float)$b[$G_ARR_SDIM]) return 0;

      return (
floatval($a[$G_ARR_SDIM]) > floatval($b[$G_ARR_SDIM])) ? 1: -1;
  }
 
/* -- Sort strings? */

 
if ($G_ARR_STYPE == 'STRING') return strcmp($a[$G_ARR_SDIM],$b[$G_ARR_SDIM]);

 
/* -- First time: get the right data type */

 
$G_ARR_STYPE = is_string($a[$G_ARR_SDIM])? 'STRING' : 'NUMBER';

  return
arr_dimsort_cmp($a,$b);
}

function
g_arr_dimsort(&$arr,$dim,$type = '',$keepkey = false)
/*
 *      Sort an array by a given dimension
 */
{
  global
$G_ARR_SDIM, $G_ARR_STYPE;

 
$G_ARR_SDIM = $dim; $G_ARR_STYPE = $type;

  if (
$keepkey) uasort(&$arr,'arr_dimsort_cmp');
  else
     
usort(&$arr,'arr_dimsort_cmp');
}

/* -- Testing it: */

$arr[] = array('Fruit' => 'Orange','Stock' => 10, 'Price'=> 10.2);
$arr[] = array('Fruit' => 'Lemon' ,'Stock' => 100,'Price'=> 5.36);
$arr[] = array('Fruit' => 'Apple' ,'Stock' => 20,'Price'=> 11.1);

echo
"Sort by Fruit\n"; g_arr_dimsort(&$arr,'Fruit'); print_r($arr);
echo
"Sort by Stock\n"; g_arr_dimsort(&$arr,'Stock','NUMBER'); print_r($arr);
echo
"Sort by Fruit and keep keys\n"; g_arr_dimsort(&$arr,'Fruit','',true); print_r($arr);
echo
"Sort by Price\n"; g_arr_dimsort(&$arr,'Price'); print_r($arr);

?>
Anonymous
12.01.2009 12:14
To sort reliably on more than one "column" in database-style arrays, handle this within your cmp function where the first field to be sorted returns 0:

<?php

function cmp($a, $b){
   
 if(
$a[field_1] == $b[field_1]){

  if(
$a[field_2] == $b[field_2]){

   return
0;

  }

  elseif(
$a[field_2] > $b[field_2]){

   return -
1;

  }

  elseif(
$a[field_2] < $b[field_2]){

   return
1;

  }

 }

 elseif(
$a[field_1] > $b[field_1]){

  return -
1;

 }

 elseif(
$a[field_1] < $b[field_1]){

  return
1;

 }

}

?>

Sorting by doing multiple passes does not seem to be reliable.
jalil at measat dot org
29.12.2008 2:37
<?php
/*---------------------------------------------------------
* my own home made easy object sort, which
* i cannot find anywhere on the net
* at all, tut. i can find only complicated ones,
* which brings me a headache.
*
* You can enhance it to return all sorts of error
* conditions if you wish,
* i need nothing but a false return upon failure to sort,
* so that is the only error i have here
*
* Example usage:
*
* INPUT                 -       $objects_array
// an array of objects for sorting

* PARAMETERS    -       property
// the property of the object to be sorted

* OUTPUT                -       $engine
// an engine object created by new,
// with your arrays of objects sorted according to your
// selected property
//( the sorting is done upon sort object creation )
*
* Code Examples
*
* $engine = new objSorter($objects_array);
// sorts on FIRST property -  default

* $engine = new objSorter($objects_array,'property');
// sort on ANY specific property
*--------------------------------------------------------*/

class objSorter
{
var
$property;
var
$sorted;

    function
ObjSorter($objects_array,$property=null)
        {
           
$sample    = $objects_array[0];
           
$vars    = get_object_vars($sample);

        if (isset(
$property))
            {
            if (isset(
$sample->$property))
// make sure requested property is correct for the object
               
{   
               
$this->property = $property;
               
usort($objects_array, array($this,'_compare'));
                }
            else
                {   
               
$this->sorted    = false;
                return;   
                }
            }
        else
            {   
                list(
$property,$var)     = each($sample);
               
$this->property         = $property;
               
usort($objects_array, array($this,'_compare'));
            }

       
$this->sorted    = ($objects_array);
        }

    function
_compare($apple, $orange)
        {
       
$property    = $this->property;
        if (
$apple->$property == $orange->$property) return 0;
        return (
$apple->$property < $orange->$property) ? -1 : 1;
        }
}
// end class
?>

USAGE EXAMPLE
DUMP ORIGINAL
user Object ( [name] => Yunihe Jopoba [age] => 27 [richness] => € 899,970 )
user Object ( [name] => Janirevi Fakejorumoa [age] => 19 [richness] => € 219,078 )
user Object ( [name] => Korejowi Mezede [age] => 95 [richness] => € 3,272 )
user Object ( [name] => Nugoaivu Jongyi [age] => 84 [richness] => € 94,853 )
user Object ( [name] => Cepomase Buaesukoyua [age] => 32 [richness] => € 677,180 )
user Object ( [name] => Vejosehona Auva [age] => 20 [richness] => € 144,540 )
user Object ( [name] => Fewame Wojuvuzo [age] => 69 [richness] => € 198,231 )
user Object ( [name] => Risuku Zagedobu [age] => 48 [richness] => € 763,860 )
user Object ( [name] => Fazurada Aayu [age] => 16 [richness] => € 302,352 )

SORTED by age

<?php
$objects               
= new ObjSorter($objects_array,'age');
if (
$objects->sorted)
{
$objects_array  = $objects->sorted;
foreach (
$objects_array as $key=>$object) { print_r($object); echo "<br />"; }
}
?>

user Object ( [name] => Fazurada Aayu [age] => 16 [richness] => € 302,352 )
user Object ( [name] => Janirevi Fakejorumoa [age] => 19 [richness] => € 219,078 )
user Object ( [name] => Vejosehona Auva [age] => 20 [richness] => € 144,540 )
user Object ( [name] => Yunihe Jopoba [age] => 27 [richness] => € 899,970 )
user Object ( [name] => Cepomase Buaesukoyua [age] => 32 [richness] => € 677,180 )
user Object ( [name] => Risuku Zagedobu [age] => 48 [richness] => € 763,860 )
user Object ( [name] => Fewame Wojuvuzo [age] => 69 [richness] => € 198,231 )
user Object ( [name] => Nugoaivu Jongyi [age] => 84 [richness] => € 94,853 )
user Object ( [name] => Korejowi Mezede [age] => 95 [richness] => € 3,272 )

SORTED by richness

<?php
$objects               
= new ObjSorter($objects_array,'richness');
if (
$objects->sorted)
{
$objects_array  = $objects->sorted;
foreach (
$objects_array as $key=>$object) { print_r($object); echo "<br />"; }
}
?>

user Object ( [name] => Vejosehona Auva [age] => 20 [richness] => € 144,540 )
user Object ( [name] => Fewame Wojuvuzo [age] => 69 [richness] => € 198,231 )
user Object ( [name] => Janirevi Fakejorumoa [age] => 19 [richness] => € 219,078 )
user Object ( [name] => Korejowi Mezede [age] => 95 [richness] => € 3,272 )
user Object ( [name] => Fazurada Aayu [age] => 16 [richness] => € 302,352 )
user Object ( [name] => Cepomase Buaesukoyua [age] => 32 [richness] => € 677,180 )
user Object ( [name] => Risuku Zagedobu [age] => 48 [richness] => € 763,860 )
user Object ( [name] => Yunihe Jopoba [age] => 27 [richness] => € 899,970 )
user Object ( [name] => Nugoaivu Jongyi [age] => 84 [richness] => € 94,853 )

Simulated wrong property request ( dumb property )

<?php
$objects               
= new ObjSorter($objects_array,'dumb property');
if (
$objects->sorted)
{
$objects_array  = $objects->sorted;
foreach (
$objects_array as $key=>$object) { print_r($object); echo "<br />"; }
}
else echo
'DUMB ERROR';
?>

DUMB ERROR
drorasta
22.06.2008 19:16
Here's how you can usort() a multidimensional array (like in example #2 above) within a class:

usort($myArray, array("className", "cmp"));
Ariz Jacinto
13.05.2008 19: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;
    }
}

?>
Anonymous
14.03.2008 13: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.

<?php
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. )
Sid_M
6.11.2007 2: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.
jc dot glombard at gmail dot com
9.10.2007 15: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);

?>
lite
28.04.2007 13: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'));

?>

15.03.2007 14: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.
rob at digital-crocus dot com
30.01.2007 16: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);
Tim
13.11.2006 12: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
svenxy AT nospam gmx net
16.10.2006 10: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.09.2006 12:51
To markus:

your function can be simplified to:

<?php
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
7.07.2006 0: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.05.2006 3:13
<?php
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 <><
16.05.2006 22:07
Refinement of arfsort to allow per-field sort direction setting:

<?php
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
6.04.2006 7: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.01.2006 10: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
23.09.2005 23: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.09.2005 16: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
15.09.2005 21: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
15.09.2005 20: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;
?>
rpmPUBLI at iespana dot es
3.09.2005 21: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
8.08.2005 15: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...

<?php
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:

<?php
usort
($array,"cmp");
?>
Jeremy Swinborne
20.07.2005 22: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.07.2005 11: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.06.2005 11: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.04.2005 1: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.03.2005 12: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
4.03.2005 16: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
4.03.2005 1: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');

?>
Raveler at telenet dot be
20.05.2004 19: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.03.2004 22: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
8.03.2004 13: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.01.2004 7: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('category_id', 'parent_id', 'category_position', $rows);

foreach(
$ordered as $item)
{
    echo
str_repeat('------', $item['indent']).$item['category_name'].'<br>';
}

/*

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

?>
sreid at sea-to-sky dot net
8.01.2004 5: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)).

<?php
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.12.2003 12: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.

<?php
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.11.2003 9: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
9.10.2003 3: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;//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
'<br>';
}

?>
skrebbel at operamail dot com
27.03.2003 22: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().

<?php
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 thing.
steveNO_SPAM at AXE_MEcontentwatch dot com
31.12.2002 23: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:

<?php
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
12.12.2002 1: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....

<?php
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
25.10.2002 1: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:

<?php
$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.

<?php
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:

<?php
sortRows
($aaRows, array(array('name' => 'STATE', 'dir' => 'ASC'), array('name' => 'CITY', 'dir' => 'DESC')));
?>
mkr at binarywerks dot dk
20.09.2002 18:29
If you want to sort an array according to another array acting as a priority list, you can use this function.

<?php
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
6.08.2002 22: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.07.2002 14: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
2.07.2002 13: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
16.01.2002 5: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.11.2001 21: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 )
{
&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.
xnoguer at rezebra dot com
25.09.2001 20: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
25.08.2001 3: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
18.07.2001 0: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.06.2001 14: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);

6.04.2001 13: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.03.2001 14: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.09.2000 8:35
Needed a date sort and I didn't know if one was available so I wrote one. Maybe it'll help someone:

<?php
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.02.2000 21: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);
   
// 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 . " ";
?>



PHP Powered Diese Seite bei php.net
The PHP manual text and comments are covered by the Creative Commons Attribution 3.0 License © the PHP Documentation Group - Impressum - mail("TO:Reinhard Neidl",...)