uasort

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

uasort --  Отсортировать массив, используя пользовательскую функцию для сравнения элементов с сохранением ключей

Описание

bool uasort ( array &array, callback cmp_function )

Функция сортирует массив таким образом, что его индексы сохраняют отношения с элементами, с которыми ранее были ассоциированы. Это особенно полезно при сортировке ассоциативных массивов, актуальный порядок элементов которых значим. Для сравнения используется функция, определённая пользователем.

Возвращает TRUE в случае успешного завершения или FALSE в случае возникновения ошибки.

Замечание: Пожалуйста, смотрите примеры пользовательских функций в описании usort() и uksort().

См. также usort(), uksort(), sort(), asort(), arsort(), ksort() и rsort().



uasort
rlynch at lynchmarks dot com
16-May-2005 05:36
A subtle bug, corrected...

<?php
function masort(&$data, $sortby)
{
   static
$sort_funcs = array();
 
   if (empty(
$sort_funcs[$sortby])) {
      
$code = "\$c=0;";
       foreach (
split(',', $sortby) as $key) {
        
$array = array_pop($data);
        
array_push($data, $array);
         if(
is_numeric($array[$key]))
          
$code .= "if ( \$c = ((\$a['$key'] == \$b['$key']) ? 0:((\$a['$key'] < \$b['$key']) ? -1 : 1 )) ) return \$c;";
         else
          
$code .= "if ( (\$c = strcasecmp(\$a['$key'],\$b['$key'])) != 0 ) return \$c;\n";
       }
      
$code .= 'return $c;';
      
$sort_func = $sort_funcs[$sortby] = create_function('$a, $b', $code);
   } else {
      
$sort_func = $sort_funcs[$sortby];
   }
  
$sort_func = $sort_funcs[$sortby];
  
uasort($data, $sort_func);
}
?>

Note that

$code .= "if ( \$c = ((\$a['$key'] == \$b['$key']) ? 0:((\$a['$key'] < \$b['$key']) ? -1 : 1 )) ) return \$c;";

Has had the "return \$c" added.  Ultimately what the method is trying to accomplish is to build a chain of sort-order precedence.  But this requires each evaluation to short-circuit out with a return.  It was missing.

Peace
lucas dot karisny at linuxmail dot org
12-Feb-2005 06:09
The following is a modification of the dholme/messju masort func using david's float code with automatic data type detection.  As long as the value isn't a string numeral (I.E. "1", '13.4') this should sort strings and numbers without having to explicity set which they are.

<?php
function masort(&$data, $sortby)
{
   static
$sort_funcs = array();
 
   if (empty(
$sort_funcs[$sortby])) {
      
$code = "\$c=0;";
       foreach (
split(',', $sortby) as $key) {
        
$array = array_pop($data);
        
array_push($data, $array);
         if(
is_numeric($array[$key]))
          
$code .= "if ( \$c = ((\$a['$key'] == \$b['$key']) ? 0:((\$a['$key'] < \$b['$key']) ? -1 : 1 )) );";
         else
          
$code .= "if ( (\$c = strcasecmp(\$a['$key'],\$b['$key'])) != 0 ) return \$c;\n";
       }
      
$code .= 'return $c;';
      
$sort_func = $sort_funcs[$sortby] = create_function('$a, $b', $code);
   } else {
      
$sort_func = $sort_funcs[$sortby];
   }
  
$sort_func = $sort_funcs[$sortby];
  
uasort($data, $sort_func);
}
?>
siefer at sym dot net
04-Oct-2004 02:24
Hello naholyr at yahoo dot fr!

should it be

$cmp = create_function('$a, $b', "return $cmp_val;");

?

this works with my arrays ;-)

regards, Christopher
marek at lewczuk dot com
27-May-2004 04:01
Below another array sorting function - you can use many keys, whether order type is ascendant or descendant, if values of given key should be treat as string or numeric, if array keys should be preserved. This function is locale-safe - it means that string sorting will be based on setLocale settings. You should be aware that I did not make comprehensive tests, so be careful...

// my locales
SetLocale(LC_COLLATE,"pl_PL.UTF-8");
SetLocale(LC_CTYPE, "pl_PL.UTF-8");

function Array_Sort ($array, $arguments = Array(), $keys = true) {
      
  // comparing function code
  $code = "";
      
  // foreach sorting argument (array key)
  foreach ($arguments as $argument) {
          
   // order field
   $field = substr($argument, 2, strlen($argument));
          
   // sort type ("s" -> string, "n" -> numeric)
   $type = $argument[0];
          
   // sort order ("+" -> "ASC", "-" -> "DESC")
   $order = $argument[1];
              
   // add "if" statement, which checks if this argument 
       should be used
   $code .= "if (!Is_Numeric(\$result) || \$result == 0) ";
              
   // if "numeric" sort type
   if (strtolower($type) == "n") $code .= $order == "-" ? "\$result = (\$a['{$field}'] > \$b['{$field}'] ? -1 : (\$a['{$field}'] < \$b['{$field}'] ? 1 : 0));" : "\$result = (\$a['{$field}'] > \$b['{$field}'] ? 1 : (\$a['{$field}'] < \$b['{$field}'] ? -1 : 0));";
          
   // if "string" sort type
   else $code .= $order == "-" ? "\$result = strcoll(\$a['{$field}'], \$b['{$field}']) * -1;" : "\$result = strcoll(\$a['{$field}'], \$b['{$field}']);";
  }
          
  // return result
  $code .= "return \$result;";
  
  // create comparing function
  $compare = create_function('$a, $b', $code);
      
  // sort array and preserve keys
  if ($keys) uasort($array, $compare);
      
  // sort array, but not preserve keys
  else usort($array, $compare);
          
  // return array
  return $array;
}

Example array:
$array['sdsd'] = array("dir" => 1, "name" => "sas", "olek" => "sdsd");
$array['sds2'] = array("dir" => 2, "name" => "&#347;as", "olek" => "t");

Example - preserve keys:
print_r(Array_Sort($array, Array("s-name", "n-dir", "s+olek")));

Array
(
   [sds2] => Array
       (
           [dir] => 1
           [name] => &#347;as
           [olek] => t
       )

   [sdsd] => Array
       (
           [dir] => 1
           [name] => sas
           [olek] => sdsd
       )

)

Example - without preserving keys:
print_r(Array_Sort($array, Array("s-name", "n-dir", "s+olek")), false);

Array
(
   [0] => Array
       (
           [dir] => 1
           [name] => &#347;as
           [olek] => t
       )

   [1] => Array
       (
           [dir] => 1
           [name] => sas
           [olek] => sdsd
       )

)

Enyoj
david [__at__] castlelaing.com
10-May-2004 09:28
WARNING:-Regarding remmy.cjb.net (22-Oct-2003 05:57) mutisort() function:

Sorting by floating point numbers doesn't work in the current function. Use the modified version below if you want to sort by a floating point column.

<?php
// Based on the other notes given before.
// Sorts an array (you know the kind) by key
// and by the comparison operator you prefer.

// Note that instead of most important criteron first, it's
// least important criterion first.

// The default sort order is ascending, and the default sort
// type is strnatcmp.

// function multisort($array[, $key, $order, $type]...)
function multisort($array)
{
   for(
$i = 1; $i < func_num_args(); $i += 3)
   {
      
$key = func_get_arg($i);
       if (
is_string($key)) $key = '"'.$key.'"';
    
      
$order = true;
       if(
$i + 1 < func_num_args())
          
$order = func_get_arg($i + 1);
    
      
$type = 0;
       if(
$i + 2 < func_num_args())
          
$type = func_get_arg($i + 2);

       switch(
$type)
       {
           case
1: // Case insensitive natural.
              
$t = 'strcasecmp($a[' . $key . '], $b[' . $key . '])';
               break;
           case
2: // Numeric.
              
$t = '($a[' . $key . '] == $b[' . $key . ']) ? 0:(($a[' . $key . '] < $b[' . $key . ']) ? -1 : 1)';
               break;
           case
3: // Case sensitive string.
              
$t = 'strcmp($a[' . $key . '], $b[' . $key . '])';
               break;
           case
4: // Case insensitive string.
              
$t = 'strcasecmp($a[' . $key . '], $b[' . $key . '])';
               break;
           default:
// Case sensitive natural.
              
$t = 'strnatcmp($a[' . $key . '], $b[' . $key . '])';
               break;
       }
       echo
$t;
      
usort($array, create_function('$a, $b', '; return ' . ($order ? '' : '-') . '(' . $t . ');'));
   }
   return
$array;
}
?>
dn dot php at gmx dot de
23-Nov-2003 04:34
regarding remmy.cjb.net (22-Oct-2003 05:57) note:
The "multisort" function is not working. Try the following example.
( I hope this and your note will be deleted soon.)
..- Denis

<?php
$a
= array(
   array(
'c1' => 1, 'c2' => 1, 'c3' => 1, 'c4' => 1),
   array(
'c1' => 1, 'c2' => 1, 'c3' => 1, 'c4' => 2),
   array(
'c1' => 1, 'c2' => 1, 'c3' => 2, 'c4' => 1),
   array(
'c1' => 1, 'c2' => 1, 'c3' => 2, 'c4' => 2)
);

echo(
'<pre>');
print_r(multisort($a, "'c4'", true, 2,"'c3'", true, 2,"'c2'", true, 2,"'c1'", true, 2));
echo(
'</pre>');
?>
remmy.cjb.net
21-Oct-2003 11:57
Hope this helps!

- Remmy

<?php

// Based on the other notes given before.
// Sorts an array (you know the kind) by key
// and by the comparison operator you prefer.

// Note that instead of most important criteron first, it's
// least important criterion first.

// The default sort order is ascending, and the default sort
// type is strnatcmp.

// function multisort($array[, $key, $order, $type]...)
function multisort($array)
{
   for(
$i = 1; $i < func_num_args(); $i += 3)
   {
      
$key = func_get_arg($i);
      
      
$order = true;
       if(
$i + 1 < func_num_args())
          
$order = func_get_arg($i + 1);
      
      
$type = 0;
       if(
$i + 2 < func_num_args())
          
$type = func_get_arg($i + 2);

       switch(
$type)
       {
           case
1: // Case insensitive natural.
              
$t = 'strcasenatcmp($a[' . $key . '], $b[' . $key . '])';
               break;
           case
2: // Numeric.
              
$t = '$a[' . $key . '] - $b[' . $key . ']';
               break;
           case
3: // Case sensitive string.
              
$t = 'strcmp($a[' . $key . '], $b[' . $key . '])';
               break;
           case
4: // Case insensitive string.
              
$t = 'strcasecmp($a[' . $key . '], $b[' . $key . '])';
               break;
           default:
// Case sensitive natural.
              
$t = 'strnatcmp($a[' . $key . '], $b[' . $key . '])';
               break;
       }

      
uasort($array, create_function('$a, $b', 'return ' . ($order ? '' : '-') . '(' . $t . ');'));
   }

   return
$array;
}

$a = array(
   array(
'id' => 1, 'name' => 'apple'),
   array(
'id' => 2, 'name' => 'orange'),
   array(
'id' => 8, 'name' => 'banana'),
   array(
'id' => 8, 'name' => 'grapefruit'),
   array(
'id' => 9, 'name' => 'smoke'),
   array(
'id' => 1, 'name' => 'screen')
);

// This works like MYSQL 'ORDER BY id DESC, name ASC'
// Note the quoting of string literal keys.
echo('<pre>');
print_r(multisort($a, "'name'", true, 0, "'id'", false, 2));
echo(
'</pre>');

?>
cablehead
16-Sep-2003 09:08
dholmes we turned your masort function into a smarty plugin:

http://www.phpinsider.com/smarty-forum/viewtopic.php?t=1079

messju contributed the following performance improvements. 

function masort(&$data, $sortby)
{
   static $sort_funcs = array();
  
   if (empty($sort_funcs[$sortby])) {
       $code = "\$c=0;";
       foreach (split(',', $sortby) as $key) {
           $code .= "if ( (\$c = strcasecmp(\$a['$key'],\$b['$key'])) != 0 ) return \$c;\n";
       }
       $code .= 'return $c;';
       $sort_func = $sort_funcs[$sortby] = create_function('$a, $b', $code);
   } else {
       $sort_func = $sort_funcs[$sortby];
   }
   $sort_func = $sort_funcs[$sortby];
   uasort($data, $sort_func);
}

thank you for the cool function!
php at eden2 dot com
17-Jul-2003 04:36
Is it just me, or are the examples below misleading, and actually demonstrating situations that would be more appropriate for usort()?

After trying to make sense of the uasort() description, it sounds like it's more for sorting a 1D array like this:

"john" => "$23.12"
"tim" => "$6.50"
"bob" => "$18.54"

and getting back:

"tim" => "$6.50"
"bob" => "$18.54"
"john" => $23.12"

(assuming, of course, that your sort function is lopping off the $ and evaluating as a number -- which would complicate the use of asort() ;)
naholyr at yahoo dot fr
10-Jan-2003 09:35
You can sort a multidimensionnal array by any of its key with this function:

function multi_sort($array, $key)
{
  $cmp_val="((\$a['$key']>\$b['$key'])?1:
   ((\$a['$key']==\$b['$key'])?0:-1))";
  $cmp=create_function('$a, $b', "return $body;");
  uasort($array, $cmp);
  return $array;
}

example:
$myarray = array(
  array("name"=>"kernighan", "language"=>"c"),
  array("name"=>"lerdorf", "language"=>"php"),
  array("name"=>"Stroustrup", "language"=>"c++"),
  array("name"=>"Gosling", "language"=>"java")
);

multi_sort($myarray, "name") returns:
name=Gosling    language=java
name=Kernighan    language=c
name=Lerdorf    language=php
name=Stroustrup    language=c++
dholmes at jccc d0t net
09-Oct-2002 02:27
Here is a little sort function that actually uses a dynamic callback for usort to do it's thing.

It assumes your data is in the form of:
   $data = array(
           array('ID'=>'6','LAST'=>'Holmes','FIRST'=>'Dan'),
           array('ID'=>'1234','LAST'=>'Smith','FIRST'=>'Agent K'),
           array('ID'=>'2','LAST'=>'Smith','FIRST'=>'Agent J'),
           array('ID'=>'4','LAST'=>'Barney','FIRST'=>'Bob'));

Now, you want to sort on one or more cols, don't you? 

masort($data, 'LAST,FIRST');
or
masort($data,array('FIRST','ID'));

Of course you could add a bunch to it (like numeric comparison if appropriate, desc/asc, etc) but it works for me.

function masort(&$data, $sortby){
   if(is_array($sortby)){
       $sortby = join(',',$sortby);
   }

   uasort($data,create_function('$a,$b','$skeys = split(\',\',\''.$sortby.'\');
       foreach($skeys as $key){
           if( ($c = strcasecmp($a[$key],$b[$key])) != 0 ){
               return($c);
           }
       }
       return($c); '));
}
Notice that I am splitting the string in the comparison function? While this is certainly slower, it was the only way I would find to "pass" and "array".  If anyone has a better way, please suggest.  Then inside, we (string) compare the values only moving to the next key if the values are the same...and so on, and so on.
stilgar_cpsNOSPAM at zipmail dot NOSPAMcom dot br
05-Sep-2001 02:30
Use example:

$array[0]['Fator1']=7;
$array[0]['Fator2']="Name";
$array[1]['Fator1']=5;
$array[1]['Fator2']="Name";
$array[2]['Fator1']=7;
$array[2]['Fator2']="NameDiferente";
.....

We want to order by Fator1, then Fator2, then:

function Compare($ar1, $ar2)
{
   if ($ar1['Fator1']<$ar2['Fator1'])
     return -1;
   else if ($ar1['Fator1']>$ar2['Fator1'])
     return 1;
   if ($ar1['Fator2']<$ar2['Fator2'])
     return -1;
   else if ($ar1['Fator2']>$ar2['Fator2'])
     return 1;
   return 0;
}

To sort now, we use:

uasort($array, 'Compare');

<sortuksort>
 Last updated: Mon, 14 Nov 2005