PHP: Sorting Arrays of Arrays

Using usort function to sort arrays of arrays

Posted on April 14, 2016 in PHP

The following example are based on the following data as an associative array.


$data = array(
    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 as:


index   firstname   lastname    age
0   Mary        Johnson     25
1   Amanda      Miller      18
2   James       Brown       31
3   Patrici     William     7
4   Michael     Davis       43
5   Sarah       Miller      24
6   Patrick     Miller      27

Sorting on a single field

Sorting associative arrays is really quite simple. For example, to sort by lastname field, you can use the usort 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:

index   firstname   lastname    age
0   James       Brown       31
1   Michael     Davis       43
2   Mary        Johnson     25
3   Amanda      Miller      18
4   Sarah       Miller      24
5   Patrick     Miller      27
6   Patrici     William     7

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');
?>

The output is following, with the result the index value maintained:


index   firstname   lastname    age
2   James       Brown       31
4   Michael     Davis       43
0   Mary        Johnson     25
1   Amanda      Miller      18
5   Sarah       Miller      24
6   Patrick     Miller      27
3   Patrici     William     7

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. It’s wrong. Since PHP version 4.1 the usort and related functions will first shuffle the array before sorting. This means that, after ordering by first name, the result ofthen sorting by last name will not necessarily retain the correct order of the first names.

The solution is 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');
?>

The output of sorting both first and last name is like:


index   firstname   lastname    age
2   James       Brown       31
4   Michael     Davis       43
0   Mary        Johnson     25
1   Amanda      Miller      18
6   Patrick     Miller      27
5   Sarah       Miller      24
3   Patrici     William     7

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 zeor value.

Calling from a class

If you are trying to sort using a function inside a class, you can’t 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 = array(
      ...
    );

    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, array($this, 'compare_fullname'));
    }

  }

  // apply sort function from OUTSIDE class
  $myObject = new ClassName();
  usort($myObject->data, array("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

You can create an anonymous function based on information gathered at run time. This allows for greater flexibility.


<?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');
?>

This output is like:


index   firstname   lastname    age
0   Patrici     William     7
1   Amanda      Miller      18
2   Sarah       Miller      24
3   Mary        Johnson     25
4   Patrick     Miller      27
5   James       Brown       31
6   Michael     Davis       43

It’s looking nice.

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 = array($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, array('lastname', 'firstname'));
?>


comments powered by Disqus