array_multisort

(PHP 4, PHP 5)

array_multisort -- Сортировать несколько массивов или многомерные массивы

Описание

bool array_multisort ( array ar1 [, mixed arg [, mixed ... [, array ...]]] )

Функция array_multisort() может быть использована для сортировки сразу нескольких массивов или одного многомерного массива в соответствии с одной или несколькими размерностями. Эта функция сохраняет соответствие между ключами и соответствующими им значениями.

Входные массивы рассматриваются как столбцы таблицы, которую нужно отсортировать по строкам - такой подход напоминает поведение выражения SQL ORDER BY. Первый массив имеет проиоритет в процессе сортировки.

Структура аргументов этой функции немного необычна, но удобна. Первым аргументом должен быть массив. Последующие аргументы могут быть как массивами, так и значениями, определяющими порядок сортировки, приведенными в нижеследующем списке.

Значения, определяющие порядок сортировки:

  • SORT_ASC - сортировать в возрастающем порядке

  • SORT_DESC - сортировать в убывающем порядке

Sorting type flags:

  • SORT_REGULAR - сравнивать элементы обычным образом

  • SORT_NUMERIC - сравнивать элементы, как если бы они были числами

  • SORT_STRING - сравнивать элементы, как если бы они были строками

Недопустимым является указание двух флагов сортировки одинакового типа после каждого массива. Флаги сортировки, переданные после аргумента массив, применяются только к этому аргументу - перед тем, как функция начнет обрабатывать следующий массив, эти флаги снова принимают значения по умолчаниюt SORT_ASC и SORT_REGULAR.

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

Пример 1. Сортировка нескольких массивов

$ar1 = array ("10", 100, 100, "a");
$ar2 = array (1, 3, "2", 1);
array_multisort ($ar1, $ar2);

В вышеприведенном примере, после того, как будет осуществлена сортировка, первый массив будет содержать 10, "a", 100, 100. Второй - 1, 1, "2", 3. Элементы второго массива, соответствующие идентичным элементам первого (100 и 100), также будут отсортированы.

Пример 2. Сортировка многомерного массива

$ar = array (array ("10", 100, 100, "a"), array (1, 3, "2", 1));
array_multisort ($ar[0], SORT_ASC, SORT_STRING,
                 $ar[1], SORT_NUMERIC, SORT_DESC);

В вышеприведенном примере, после сортировки, первый массив будет содержать 10, 100, 100, "a" (его элементы были отсортированы в возрастающем порядке так, как если бы они были строками), а второй массив будет содержать 1, 3, "2", 1 (элементы отсортированы как числа, в порядке убывания).



array_multisort
Cesar Sirvent
17-May-2006 09:43
There is a problem with array_multisort in languages other than English.
For special chars, as A with accent (Á), the sorting does not correspond to what might expect from a MySQL SELECT with ORDER BY.

For example

<?php
 
      
foreach ($students as $key => $row){
        
$surname[$key] = $row['surname'];
       }
      
array_multisort($surname, SORT_ASC, $students);
?>

will sort the array in this way: ABADIA, ALVAREZ, BUÑUEL, ZUBIETA, ÁLVARES

while a MySQL SELECT with ORDER BY nombre ASC will yield

ABADIA, ÁLVARES, ALVEREZ, BUÑUEL, ZUBIETA

as A and Á are considered two different representations of the same letter.
scott - evolove - net - work it out
22-Mar-2006 07:51
A very simple way to sort an array of associative arrays by some value is to use usort.

I needed to sort an array of 20 data structures by their 'distance' value:

Array
(
   [0] => Array
       (
           [blahblah] => blahblah
           [distance] => 6
       )

   [1] => Array
       (
         you get the idea....

Here's the code:

--------------------
usort($results, "distributor_compare");

/**
 * usort callback
 */
function distributor_compare($a, $b) {
   $adist = intval($a['distance']);
   $bdist = intval($b['distance']);
  
   if ($adist == $bdist) {
     return 0;
     }
     return ($adist < $bdist) ? -1 : 1;   
}
--------------------
peter dot graham at tcat dot ac dot uk
16-Feb-2006 07:16
Many thanks to AlberT at SuperAlberT dot it for his useful and elegant function. I have made some small alterations so it doesnt use pass-by-reference as this throws up warnings in newer versions of php4.

<?php

function array_key_multi_sort($arr, $l , $f='strnatcasecmp')
{
  
usort($arr, create_function('$a, $b', "return $f(\$a['$l'], \$b['$l']);"));
   return(
$arr);
}

?>
mail at theopensource dot com
31-Jan-2006 11:34
I wanted to share with you a function that I created to make the array_multisort process much easier for myself... There was some interesting things that I encountered and I will post that in the comments.

I created this function so that all I have to do is tell it what column I want to sort through in a one level deep multidimensional array.  You can Try this code in your browser to view the results

ex/
<?php

//Here is an array example
$test[0]['name'] = "David";
$test[0]['age'] = 28;
$test[1]['name'] = "Dennis";
$test[1]['age'] = 23;
$test[2]['name'] = "Joseph";
$test[2]['age'] = 42;

//Here is the Function

function sortmddata($array, $by, $order, $type){

//$array: the array you want to sort
//$by: the associative array name that is one level deep
////example: name
//$order: ASC or DESC
//$type: num or str
      
$sortby = "sort$by"; //This sets up what you are sorting by

$firstval = current($array); //Pulls over the first array

$vals = array_keys($firstval); //Grabs the associate Arrays

foreach ($vals as $init){
  
$keyname = "sort$init";
   $
$keyname = array();
}
//This was strange because I had problems adding
//Multiple arrays into a variable variable
//I got it to work by initializing the variable variables as arrays
//Before I went any further

foreach ($array as $key => $row) {
  
foreach (
$vals as $names){
  
$keyname = "sort$names";
  
$test = array();
  
$test[$key] = $row[$names];
   $
$keyname = array_merge($$keyname,$test);
  
}

}

//This will create dynamic mini arrays so that I can perform
//the array multisort with no problem
//Notice the temp array... I had to do that because I
//cannot assign additional array elements to a
//varaiable variable           

if ($order == "DESC"){   
if (
$type == "num"){
array_multisort($$sortby,SORT_DESC, SORT_NUMERIC,$array);
} else {
array_multisort($$sortby,SORT_DESC, SORT_STRING,$array);
}
} else {
if (
$type == "num"){
array_multisort($$sortby,SORT_ASC, SORT_NUMERIC,$array);
} else {
array_multisort($$sortby,SORT_ASC, SORT_STRING,$array);
}
}

//This just goed through and asks the additional arguments
//What they are doing and are doing variations of
//the multisort

return $array;
}

//Now to test it

$test = sortmddata($test,'age','ASC','num');

print_r ($test);

//This will return
//Array (
//[0] => Array ([name] => Dennis [age] => 23 )
//[1] => Array ( [name] => David [age] => 28 )
//[2] => Array ( [name] => Joseph [age] => 42 )
//)

?>

There you go... please let me know what you think if you like.
php a-t-the-r-a-t-e chir.ag
05-Jan-2006 02:10
Re: phu at kungphu, 19-Dec-2005 11:36

asort($test) will not let me specify which columns to sort ASC/DESC, NUMERIC/STRING etc.

I have data similar to what you specified. Now I want to sort $test by points DESC and name ASC. Here's my function that does it, based on suggestions on this page. It uses array_multisort (and hence acts just like it: preserving string-keys etc.)

<?php

 
function arrayColumnSort()
  {
  
$n = func_num_args();
  
$ar = func_get_arg($n-1);
   if(!
is_array($ar))
     return
false;

   for(
$i = 0; $i < $n-1; $i++)
    
$col[$i] = func_get_arg($i);

   foreach(
$ar as $key => $val)
     foreach(
$col as $kkey => $vval)
       if(
is_string($vval))
         ${
"subar$kkey"}[$key] = $val[$vval];

  
$arv = array();
   foreach(
$col as $key => $val)
    
$arv[] = (is_string($val) ? ${"subar$key"} : $val);
  
$arv[] = $ar;

  
call_user_func_array("array_multisort", $arv);
   return
$ar;
  }

 
$test["pete"]['points']=1;
 
$test["pete"]['name']='Peter';

 
$test["mike"]['points']=5;
 
$test["mike"]['name']='Mike';

 
$test["zoo"]['points']=2;
 
$test["zoo"]['name']='John Zoo';

 
$test["ab"]['points']=2;
 
$test["ab"]['name']='John Ab';

 
$test1 = $test;

 
asort($test1);

 
$test2 = arrayColumnSort("points", SORT_DESC, SORT_NUMERIC, "name", SORT_ASC, SORT_STRING, $test);

 
print_r($test1); // asort
 
print_r($test2); // arrayColumnSort

?>

Output from asort:

Array
(
   [pete] => Array
       (
           [points] => 1
           [name] => Peter
       )

   [ab] => Array
       (
           [points] => 2
           [name] => John Ab
       )

   [zoo] => Array
       (
           [points] => 2
           [name] => John Zoo
       )

   [mike] => Array
       (
           [points] => 5
           [name] => Mike
       )

)

Output from arrayColumnSort:

Array
(
   [mike] => Array
       (
           [points] => 5
           [name] => Mike
       )

   [ab] => Array
       (
           [points] => 2
           [name] => John Ab
       )

   [zoo] => Array
       (
           [points] => 2
           [name] => John Zoo
       )

   [pete] => Array
       (
           [points] => 1
           [name] => Peter
       )

)
phu at kungphu
19-Dec-2005 11:36
Notepad's example using asort/arsort will -not- work unless 'points' is defined -before- 'name'.

Running the posted code sorts by 'name', which was not the point of the previous posts.  Defining 'points' as 'apoints' also did not work; however, defining 'points' first yields correct sorting:

$test[0]['points']=1;
$test[0]['name']='Peter';

$test[1]['points']=5;
$test[1]['name']='Mike';

$test[2]['points']=2;
$test[2]['name']='John';

asort($test);

It appears asort uses the first defined element to sort a multidimensional array.
notepad at codewalkers dot com
09-Dec-2005 04:33
for you guys trying to sort scores on an associative multi-dimensional array, why are you creating your own functions?

<?php

$test
[0]['name']='Peter';
$test[0]['points']=1;

$test[1]['name']='Mike';
$test[1]['points']=5;

$test[2]['name']='John';
$test[2]['points']=2;

asort($test);
// or even arsort();

?>

the above seems to work for me...
Shmee
30-Sep-2005 06:46
RWCs code works very well until you get a point value greater then ten.  This is because the strcmp() function will return that 2 > 10 and 2 > 11 2 > 19 and so on. To compare number values over ten try a sort like this:

$test = multi_sort($test, $key = 'points');

function multi_sort($array, $akey)
{
  function compare($a, $b)
  {
     global $key;
     if ($a[$key]>$b[$key]){
         $varcmp = "1";
         return $varcmp;
     }
     elseif ($a[$key]<$b[$key]){
         $varcmp = "-1";
         return $varcmp;
     }
     elseif ($a[$key]==$b[$key]){
         $varcmp = "0";
         return $varcmp;
     }
  }
  usort($array, "compare");
  return $array;
}
RWC
25-Sep-2005 01:45
This is the simpler version of the function by AlberT.

A lot of times you have got an array like this:

$test[0]['name']='Peter';
$test[0]['points']=1;

$test[1]['name']='Mike';
$test[1]['points']=5;

$test[2]['name']='John';
$test[2]['points']=2;

You just want to sort on the index in the second dimension, ie. on points in the above example.
 
You can use the function below and call it like this:

$test = multi_sort($test, $key = 'points');

function multi_sort($array, $akey)

  function compare($a, $b)
  {
     global $key;
     return strcmp($a[$key], $b[$key]);
  }
  usort($array, "compare");
  return $array;
}

Note: to be able to use $key in the compare function, it can not simply be passed as a parameter. It has to be declared global and set somewhere outside of compare().
AlberT at SuperAlberT dot it
18-Jul-2005 06:59
a nice piece of code to do an "array_key_multi_sort()" is the following:

<?php
/**
 * orders a multidimentional array on the base of a label-key
 *
 * @param $arr, the array to be ordered
 * @param $l the "label" identifing the field
 * @param $f the ordering function to be used,
 *    strnatcasecmp() by default
 * @return  TRUE on success, FALSE on failure.
 */
function array_key_multi_sort(&$arr, $l , $f='strnatcasecmp') {
       return
usort($arr, create_function('$a, $b', "return $f(\$a['$l'], \$b['$l']);"));
}
?>
kencomer at kencomer dot com
12-Jun-2005 11:48
If your data is in an associative array that you would need to separate into columns such as those retrieved with your favorite flavor of _fetch_array() in order to be able to use multisort(), you should consider using uasort() or usort() as an alternative. Here is an example of how this could be beneficial:

(key is employee number which remains intact; rows are sorted by surname, firstname)

<?php
function namecmp( $row1,$row2 )
{
  
$first = strcmp($row1['surname'], $row2['surname']) ;
   if (
$first )
       return
$first ;
   else
       return
strcmp($row1['name'], $row2['name']) ;
}

    
$test = array( '11122202' => array('empno'=>11122202,'name'=>'geezer'
              
,'surname'=>'schmidt','age'=>96,'sex'=>'male')
           ,
'11122204' => array('empno'=>11122204,'name'=>'coed'
              
,'surname'=>'beaujolais','age'=>18,'sex'=>'female')
           ,
'11122206' => array('empno'=>11122206,'name'=>'immortal'
              
,'surname'=>'ramos','age'=>21,'sex'=>'male')
           ,
'11122208' => array('empno'=>11122208,'name'=>'babyface'
              
,'surname'=>'brown','age'=>1,'sex'=>'male')
           ,
'11122210' => array('empno'=>11122210,'name'=>'exjock'
              
,'surname'=>'gatti','age'=>48,'sex'=>'male')
           ,
'11122212' => array('empno'=>11122212,'name'=>'jailbait'
              
,'surname'=>'muhammed','age'=>15,'sex'=>'female') )
           ;
echo
"<pre>" ;
print_r( $test ) ;
uasort($test,'namecmp') ;
echo
"----sorting----<br \>";
print_r( $test ) ;
echo
"</pre>" ;

?>

Array
(
   [11122202] => Array
       (
           [empno] => 11122202
           [name] => geezer
           [surname] => schmidt
           [age] => 96
           [sex] => male
       )

   [11122204] => Array
       (
           [empno] => 11122204
           [name] => coed
           [surname] => beaujolais
           [age] => 18
           [sex] => female
       )

   [11122206] => Array
       (
           [empno] => 11122206
           [name] => immortal
           [surname] => ramos
           [age] => 21
           [sex] => male
       )

   [11122208] => Array
       (
           [empno] => 11122208
           [name] => babyface
           [surname] => brown
           [age] => 1
           [sex] => male
       )

   [11122210] => Array
       (
           [empno] => 11122210
           [name] => exjock
           [surname] => gatti
           [age] => 48
           [sex] => male
       )

   [11122212] => Array
       (
           [empno] => 11122212
           [name] => jailbait
           [surname] => muhammed
           [age] => 15
           [sex] => female
       )

)
----sorting----
Array
(
   [11122204] => Array
       (
           [empno] => 11122204
           [name] => coed
           [surname] => beaujolais
           [age] => 18
           [sex] => female
       )

   [11122208] => Array
       (
           [empno] => 11122208
           [name] => babyface
           [surname] => brown
           [age] => 1
           [sex] => male
       )

   [11122210] => Array
       (
           [empno] => 11122210
           [name] => exjock
           [surname] => gatti
           [age] => 48
           [sex] => male
       )

   [11122212] => Array
       (
           [empno] => 11122212
           [name] => jailbait
           [surname] => muhammed
           [age] => 15
           [sex] => female
       )

   [11122206] => Array
       (
           [empno] => 11122206
           [name] => immortal
           [surname] => ramos
           [age] => 21
           [sex] => male
       )

   [11122202] => Array
       (
           [empno] => 11122202
           [name] => geezer
           [surname] => schmidt
           [age] => 96
           [sex] => male
       )

)
Michael Oelze at M (myname) T GMXdotDE
25-Apr-2005 09:15
I try array_multisort at first and it seems to work well. But then I program a page with cached datas (the arrays were saved in a session). Suddenly the sort function doesn't seem to work. There was no error in my script, so I try the reference on the variables. Then he manage to sort the arrays again.

So it seems that if you save your Arrays in a session your arrays are global variables and then 'array_multisort' doesn't sort the arrays despite it gives back 'true'.
 You must reference your variables and all work well:

session_start(); // restore variables (Arrays etc.)
.....
.....
array_multisort(&$Array1,&$Array2,&$Array3,...);
....
$_SESSION["Array1"]=$Array1;//
$_SESSION["Array2"]=$Array2;//Save Arrays in Session Data
$_SESSION["Array3"]=$Array3;//
......

Now you can store your Data Arrays in your Session and are able to sort them as you like...
stevec at NO-/ dot /-SPAM dot qiguang dot net
13-Apr-2005 08:19
Be careful when using array_multisort() on copies of arrays, as you might end up changing the original array.  Given the following code:

<?php
   $test1
= array(4,3,2,1);
  
$test2 = $test1;
  
$test3 = array('a', 'b', 'c', 'd');

  
array_multisort($test2, SORT_ASC, $test3);

   echo
'test1:';
  
print_r($test1);
   echo
'test2:';
  
print_r($test2);
   echo
'test3:';
  
print_r($test3);
?>

You would expect:

test1:Array
(
   [0] => 4
   [1] => 3
   [2] => 2
   [3] => 1
)
test2:Array
(
   [0] => 1
   [1] => 2
   [2] => 3
   [3] => 4
)
test3:Array
(
   [0] => d
   [1] => c
   [2] => b
   [3] => a
)

However, if you run the code, you actually get:

test1:Array
(
   [0] => 1
   [1] => 2
   [2] => 3
   [3] => 4
)
test2:Array
(
   [0] => 1
   [1] => 2
   [2] => 3
   [3] => 4
)
test3:Array
(
   [0] => d
   [1] => c
   [2] => b
   [3] => a
)

Note that the original ($test1) ends up being sorted even though it was never called by array_multisort().  To work around this, insert a statement to modify the copy ($test2) before calling array_multisort() on it.  The following code will produce the expected "correct" results:

<?php
   $test1
= array(4,3,2,1);
  
$test2 = $test1;
  
$test3 = array('a', 'b', 'c', 'd');

  
$test2[0] = $test2[0];                // fix
  
array_multisort($test2, SORT_ASC, $test3);
      
   echo
'test1:';
  
print_r($test1);
   echo
'test2:';
  
print_r($test2);
   echo
'test3:';
  
print_r($test3);
?>

This seems to be a resurrection of the closed bug #8130.  Also, someone reported this behavior in bug #32031, but it was incorrectly labeled "bogus" in reference to bug #25359, which is a different issue.
joao at intrasystems dot com dot br
07-Apr-2005 12:27
Exemple of sorting multi-dimensional arrays by one of it's fields:

$result[0]['nome']='Joao';
$result[0]['order']=5;
$result[1]['nome']='Pedro';
$result[1]['order']=1;
$result[2]['nome']='Marcelo';
$result[2]['order']=3;

foreach($result as $res)
     $sortAux[] = $res['order'];

array_multisort($sortAux, SORT_ASC, $result);

print_r($result);

produces:

Array
(
   [0] => Array
       (
           [nome] => Pedro
           [order] => 1
       )

   [1] => Array
       (
           [nome] => Marcelo
           [order] => 3
       )

   [2] => Array
       (
           [nome] => Joao
           [order] => 5
       )

)
Toni
10-Nov-2004 03:30
An example to sort an array by 3 criteria:

$recordset is an array of results from a query to a database, that I sort using $matrix as auxiliary array. First numeric, Second and Third alphabetically.

<?php
for($idx=0;$idx<$toShow;$idx++){
  
$matrix[0][$id]=$recordset[$id][0];
  
$matrix[1][$id]=$recordset[$id][1];
  
$matrix[2][$id]=$recordset[$id][10];   
}

array_multisort($matrix[0], SORT_DESC, SORT_NUMERIC,$matrix[1], SORT_STRING, SORT_ASC,$matrix[2], SORT_STRING, SORT_ASC);
?>
meddle at dzygn.com
05-Oct-2004 07:40
If you want to sort a multidomensional array by key name you cannot use array_multisort. ie: for an array named $archivos that prints like this:

Array
(
   [0] => Array
       (
           [index] => 0
           [name] => test
       )

   [1] => Array
       (
           [index] => 0
           [name] => watertaxi.jpg
       )

   [2] => Array
       (
           [index] => 0
           [name] => 2_0003.JPG
       )

   [3] => Array
       (
           [index] => 0
           [name] => 24A_0025.JPG
       )

   [4] => Array
       (
           [index] => 1
           [name] => _CIMG3501.JPG
       )

)

If I wanted to order by "name" I'd use:

function comparar($a, $b) {
       return strnatcasecmp($a["name"], $b["name"]);
}
usort($archivos, "comparar");

This function performs a case insensitive string comparison using a "natural order" algorithm (strnatcasecmp), resulting in:

Array
(
   [0] => Array
       (
           [index] => 0
           [name] => 2_0003.JPG
       )

   [1] => Array
       (
           [index] => 0
           [name] => 24A_0025.JPG
       )

   [2] => Array
       (
           [index] => 0
           [name] => test
       )

   [3] => Array
       (
           [index] => 0
           [name] => watertaxi.jpg
       )

   [4] => Array
       (
           [index] => 1
           [name] => _CIMG3501.JPG
       )

)
kat dot n0spam at audiogalaxy dot com
23-Aug-2001 10:25
If you're having problems with array_multisort changing variables in global space when it is called inside a function and you're not passing in the function parameters by reference you can alleviate the problem by calling array_multisort with its parameters by reference.

array_multisort(&$a, SORT_DESC, &$b);

More details here in my bug report:
bugs.php?id=12936

<array_mergearray_pad>
 Last updated: Tue, 15 Nov 2005