skip to content

PHP: Sorting Arrays of Arrays

PHP provides a range of functions for sorting data based on either the key or value of an associative array. Where it gets complicated is when you need to sort an array of associative arrays by one or more conditions.

This is a very common task for PHP programmers as data returned from database/SQL queries often appears in associative array format (i.e. using pg_fetch_assoc and mysql_fetch_assoc).

The array-multisort function provides an equivalent functionality to the code presented here, but is rather convoluted to use.

The Data

The following examples are based on the following data as an associative array. The data contains six records, each of which has a value for "firstname", "lastname" and for "age".

<?PHP $data = [ array("firstname" => "Mary", "lastname" => "Johnson", "age" => 25), array("firstname" => "Amanda", "lastname" => "Miller", "age" => 18), array("firstname" => "James", "lastname" => "Brown", "age" => 31), array("firstname" => "Patricia", "lastname" => "Williams", "age" => 7), array("firstname" => "Michael", "lastname" => "Davis", "age" => 43), array("firstname" => "Sarah", "lastname" => "Miller", "age" => 24), array("firstname" => "Patrick", "lastname" => "Miller", "age" => 27) ]; ?>

This dataset can be represented in tabular format as:

indexfirstnamelastnameage
[0]MaryJohnson25
[1]AmandaMiller18
[2]JamesBrown31
[3]PatriciaWilliams7
[4]MichaelDavis43
[5]SarahMiller24
[6]PatrickMiller27

Let's start sorting!

Sorting on a single field

Sorting associative arrays is really quite simple - IF you know beforehand which field you want to sort by. For example, to sort by lastname you can use the usort (user-defined search) function:

<?PHP function compare_lastname($a, $b) { return strnatcmp($a['lastname'], $b['lastname']); } // sort alphabetically by name usort($data, 'compare_lastname'); ?>

The output is as follows:

indexfirstnamelastnameage
[0]JamesBrown31
[1]MichaelDavis43
[2]MaryJohnson25
[3]AmandaMiller18
[4]SarahMiller24
[5]PatrickMiller27
[6]PatriciaWilliams7

To sort by different fields just replace the compare_lastname function with a function that orders by firstname (see below), by age, or any other field in the associative array. The strnatcmp ("natural order" string comparison) function is handy here as it can be applied to numbers as well as strings.

Below we will explore using a single function to sort by different fields by specifying the sort field in the function call. The comparison function can also be generated on the fly as an anonymous function.

Maintaining index association

To maintain index association, replace usort with uasort in the code:

<?PHP function compare_lastname($a, $b) { return strnatcmp($a['lastname'], $b['lastname']); } // sort alphabetically by name uasort($data, 'compare_lastname'); ?>

with the result that the array values can be referenced using the original index values:

indexfirstnamelastnameage
[2]JamesBrown31
[4]MichaelDavis43
[0]MaryJohnson25
[1]AmandaMiller18
[5]SarahMiller24
[6]PatrickMiller27
[3]PatriciaWilliams7

This is useful when the 'index' is a database id that needs to stay associated with each record.

Sorting on multiple fields

If you want to sort by lastname AND firstname, then you might think that just applying two sorts in sequence would give you the desired result:

<?PHP function compare_firstname($a, $b) { return strnatcmp($a['firstname'], $b['firstname']); } function compare_lastname($a, $b) { return strnatcmp($a['lastname'], $b['lastname']); } // sort alphabetically by firstname, then by lastname usort($data, __NAMESPACE__ . '\compare_firstname'); usort($data, __NAMESPACE__ . '\compare_lastname'); ?>

Note that here we've specifed the current namespace __NAMESPACE__ to make the code compatible with PHP namespaces. Otherwise any function you pass has to exist in the global namespace.

You might think so, but since PHP version 4.1 the usort and related functions will essentially shuffle the array before sorting (the algorithm is 'no longer stable'):

If two members compare as equal, their order in the sorted array is undefined

See our Quick Sort Algorithm article for more detail.

This means that, after ordering by first name, the result of then sorting by last name will not necessarily retain the correct order of first names.

So what's the solution? We need a new function that can compare both fields at the same time and then apply the sort:

<?PHP function compare_fullname($a, $b) { // sort by last name $retval = strnatcmp($a['lastname'], $b['lastname']); // if last names are identical, sort by first name if(!$retval) $retval = strnatcmp($a['firstname'], $b['firstname']); return $retval; } // sort alphabetically by firstname and lastname usort($data, __NAMESPACE__ . '\compare_fullname'); ?>

Finally, the result we wanted:

indexfirstnamelastnameage
[0]JamesBrown31
[1]MichaelDavis43
[2]MaryJohnson25
[3]AmandaMiller18
[4]PatrickMiller27
[5]SarahMiller24
[6]PatriciaWilliams7

This function works because it first compares the last names, and only if they are identical will it compare the first names to work out the correct order.

You can extend this function to use more variables by inserting more conditions every time $retval has a zero value.

Calling from a class

If you are trying to sort using a function inside a class, you can't just pass the comparison function as '$this->compare_fullname', but instead need to include either the object or the class name in the call to usort. Here's an example:

<?PHP class ClassName { public $data = [ ... ]; public function compare_fullname($a, $b) { $retval = strnatcmp($a['lastname'], $b['lastname']); if(!$retval) return strnatcmp($a['firstname'], $b['firstname']); return $retval; } public function doStuff() { // apply sort function from INSIDE class usort($this->data, [$this, 'compare_fullname']); } } // apply sort function from OUTSIDE class $myObject = new ClassName(); usort($myObject->data, ["ClassName", 'compare_fullname']); ?>

From inside the class $this refers to both the object as well as the class and all associated functions. From outside we can't use $this so have to refer to the class by name.

Using an 'anonymous' function

This is where it starts to get interesting. You can create an anonymous function based on information gathered at run time. This allows for greater flexibility, without too much extra coding.

<?PHP function makeSortFunction($field) { $code = "return strnatcmp(\$a['$field'], \$b['$field']);"; return create_function('$a,$b', $code); } $compare = makeSortFunction('age'); usort($data, $compare); ?>

The call can then be shortened to:

<?PHP usort($data, makeSortFunction('age')); ?>

Finally, you can wrap it all into a single function as shown below. Now the code's starting to look more 'readable' which is a good sign:

<?PHP function orderBy($data, $field) { $code = "return strnatcmp(\$a['$field'], \$b['$field']);"; usort($data, create_function('$a,$b', $code)); return $data; } $sorted_data = orderBy($data, 'age'); ?>
indexfirstnamelastnameage
[0]PatriciaWilliams7
[1]AmandaMiller18
[2]SarahMiller24
[3]MaryJohnson25
[4]PatrickMiller27
[5]JamesBrown31
[6]MichaelDavis43

We now have a simple generic function that can be used to sort any associative array on a single scalar attribute.

Using 'pass by reference'

We can also pass the $data variable by reference and no longer worry about returning a value from the function. This is how built-in functions such as usort work (see the link below for more details from the PHP website).

<?PHP function orderBy(&$data, $field) { $code = "return strnatcmp(\$a['$field'], \$b['$field']);"; usort($data, create_function('$a,$b', $code)); } orderBy($data, 'age'); ?>

Simply adding the & to the function definition means that instead of passing a copy of $data to the function, a reference or pointer is passed. That means that any manipulation of the array within the function will also apply outside the function so no return command is required.

The function is otherwise equivalent to the 'pass by value' version.

create_function is deprecated

As of PHP 8 you can no longer use create_function so the previous code needs to be updated as follows:

<?PHP function orderBy($data, $field) { usort($data, function($a, $b) use ($field) { return strnatcmp($a[$field], $b[$field]); }); return $data; } ?>

And similarly for the 'pass by reference' version. Note that the keyword use in this code brings the $field variable into the scope of the anonymous function.

Sorting based on a list of values

There are situations where it's not possible to use a numeric or alphabet-based sort routine. Suppose that you have the following data to present on a website:

indexnameposition
[0]Mary JohnsonSecretary
[1]Amanda MillerMember
[2]James BrownMember
[3]Patricia WilliamsMember
[4]Michael DavisPresident
[5]Sarah MillerVice-President
[6]Patrick MillerMember

And that you want to sort these names by their position, based on the following list:

<?PHP $sortorder = [ 'President', 'Vice-President', 'Secretary', 'Member' ]; ?>

Again, it's not as difficult as you might think. All we need is the following custom function:

<?PHP function mySort($a, $b) { global $sortorder; if($a['position'] == $b['position']) { return strcmp($a['name'], $b['name']); } $cmpa = array_search($a['position'], $sortorder); $cmpb = array_search($b['position'], $sortorder); return ($cmpa > $cmpb) ? 1 : -1; } ?>

If you don't have a second field that you want to sort by, just replace the highlighted line with return 0;

All we're doing here is looking at the values in the position field. If they're identical then we sort by the name field instead. Otherwise we do a comparison of where in the sortorder list the relevant values appear and sort based on that. The output is as follows:

indexnameposition
[0]Michael DavisPresident
[1]Sarah MillerVice-President
[2]Mary JohnsonSecretary
[3]Amanda MillerMember
[4]James BrownMember
[5]Patricia WilliamsMember
[6]Patrick MillerMember

As detailed above for simpler cases we can now enhance this function to make it more generic and re-usable, but I'll leave that as an exercise for you. You would need pass the sortorder array to the function along with details of which fields to sort by.

If you're working on the web with this type of data, you might also want to read the article Formatting Data as HTML which presents instructions for converting PHP data to HTML tables, lists and forms.

Multiple row sorting using a closure

Thomas Heuer from Germany has been kind enough to provide the following code and examples, which looks very nice, but will only work with PHP 5.3 and higher:

<?PHP function sortArray($data, $field) { if(!is_array($field)) { $field = [$field]; } usort($data, function($a, $b) use($field) { $retval = 0; foreach($field as $fieldname) { if($retval == 0) $retval = strnatcmp($a[$fieldname],$b[$fieldname]); } return $retval; }); return $data; } ?>

Example calls:

<?PHP $data = sortArray($data, 'age'); $data = sortArray($data, ['lastname', 'firstname']); ?>

References

< PHP

User Comments

Post your comment or question

10 December, 2019

Hi, How to sort by last name an array like this:
$array = array(
array("name" => "Mary Johnson","age" => 43),
array("name" => "Amanda Miller","age" => 23),
array("name" => "James Brown","age" => 47),
array("name" => "Patricia Williams","age" => 31),
array("name" => "Michael Davis","age" => 15),
array("name" => "Sarah Miller","age" => 35),
array("name" => "Patrick Miller","age" => 44)
);

is it possible? thank you

Yes. You just need a sorting function that first extracts the last names, and then compares them.

7 November, 2018

Thank you!

"Sorting based on a list of values" is the one for me. Awesome!

15 June, 2016

This is nice article but I am not able to sort in descending order by perticular field(i.e. last name or age), it gives me an error "rsort() expects parameter 2 to be long, string given". Please help me. Thanks in advance.

The easiest way to reverse the sort is to just switch '$a' and '$b' in the comparison function.

30 May, 2014

Hi, could you help me with sorting array of array with 2nd order-parameter that is shuffle?

$data = array(
array("firstname" => "Mary", "age" => 25),
array("firstname" => "Amanda", "age" => 20),
array("firstname" => "James", "age" => 18),
array("firstname" => "Mary", "age" => 23)
);

and I would like to sort it by "firstname" and then, in the case that there is some "firstname" equal to another "firstname", I would like to have a random.

Could you help me with this?

Since PHP 4.1 the order of the sorted array when some values are equal is 'undefined', which can mean that it's random already.

To force the order to be random you can use something like this:

<?PHP
function sort_name_shuffle($a, $b)
{
$retval = strnatcmp($a['firstname'], $b['firstname']);
if(!$retval) return mt_rand(0,1) ? -1 : 1;
return $retval;
}
// sort alphabetically by firstname and shuffle
usort($data, 'sort_name_shuffle');
?>

24 May, 2014

I need to sort an array on two fields, but one is numeric and the other is a string. How would I do this

31 March, 2014

You could also make use of some closures to make your sorting more flexible and composable:

function stabilize_sorters() {
$args = func_get_args();
return function($a, $b) use ($args) {
$retVal = 0;
for ($i = 0; $i < count($args); $i++) {
$fn = $args[$i];
if (0 == $retVal) $retVal = $fn($a, $b);
else break;
}

return $retVal;
};
}

function create_sorter($key, $fn) {
return function($a, $b) use ($key, $fn) {
return call_user_func_array($fn, [$a[$key], $b[$key]]);
};
}

//then you may use it like
usort($anArray,
stabilize_sorters(
create_sorter('name', 'strnatcmp'), create_sorter('age', function($a, $b) {
//some weird comparison you may want to apply to age
}))
);

Yes, I would use something like this now. The article was written well before closures were available in PHP

18 January, 2012

I took staticfloat's code and made it work (some of the quotes were messed up):

function sortByKeys(&$arr, $fields)
{
if(count($fields) == 0 || count($arr) <= 1) return;
if(!is_array($fields)) $fields = Array($fields);
$code = '$retval = strnatcmp($a["'.$fields[0].'"], $b["'.$fields[0].'"]);';
for($i=1; $i < count($fields); $i++) {
$code .= 'if(!$retval) $retval = strnatcmp($a["'.$fields[$i].'"], $b["'.$fields[$i].'"]);';
}
$code .= 'return $retval;';
usort($arr, create_function('$a,$b', $code));
}

Thanks for the great post!

28 December, 2010

Well I implemented #2 and it worked as you said it would, but to be honest, I don't understand why. Could you please explain; First of all, a function compare_lastname($a, $b) is defined with 2 args, then its called with no args. That shouldn't even work, and what I don't understand even more than that is how the function even knows what $a and $b are, because they are never defined anywhere. Explaining these two concepts would really help my understanding of php. I'm kinda javaish. Thank You

I'll try. The usort and similar sorting functions in PHP work by applying a comparison function on successive pairs of values in the array to be sorted. So $a and $b are just references to the two elements being compared each time.

Maybe our JavaScript articles on sorting algorithms will help?

8 December, 2010

wow great!
I had problems with finding the correct function for sorting my arrays. With this it was easy peasy tnx!

1 September, 2010

Is it also possible to sort the array descending?

Sure. Just swap $a and $b in the strnatcmp() function calls. Or in the sort function definition (but not both)

25 June, 2010

Based on your examples, I've put that all together, having an array-sorting-function for single or multiple row-sorting - using a closure.

This will only work with PHP 5.3+

Thanks heaps. I've added your function to the page

5 March, 2010

Great Freaking article. You a life saver.

25 November, 2009

Extremely useful article! Makes sorting associative arrays so easy, especially when trying to sort on multple keys!

17 June, 2009

Great functions!!You saved my times a lot!!

23 December, 2008

A small improvement; combines a few concepts, and allows for a variable number of fields to search for;

function awesomeSort(&$data, $fields) {
//Error checking, if it's a non-sortable list or $fields is empty
if( count( $fields ) == 0 or count( $data ) <= 1 ) return;
if( !is_array( $fields ) ) $fields = Array( $fields );
$code = "$retval = strnatcmp($a["".$fields[0].""], $b["".$fields[0].""]);";
for( $i=1;$i<count($fields);$i++ )
$code .= "if( !$retval ) $retval = strnatcmp($a["".$fields[$i].""], $b["".$fields[$i].""]);";
$code .= "return $retval;";
usort( $data, create_function( '$a,$b', $code ) );
}

15 August, 2008

Very good post on handy use of built-in functions. It can perform array sorting tasks easily and quickly with less code.

15 November, 2007

Quick question for you - is it possible to expand the samples to sort by three columns?

Yes, of course. The code will be something like this (untested):

function compare_three_columns($a, $b)
{
$retval = strnatcmp($a['col1'], $b['col1']);
if(!$retval) $retval = strnatcmp($a['col2'], $b['col2']);
if(!$retval) $retval = strnatcmp($a['col3'], $b['col3']);
return $retval;
}

2 November, 2007

Thanks for this article saved me some time.

top