Sort multidimensional array by multiple columns
You need array_multisort
$mylist = array( array('ID' => 1, 'title' => 'Boring Meeting', 'event_type' => 'meeting'), array('ID' => 2, 'title' => 'Find My Stapler', 'event_type' => 'meeting'), array('ID' => 3, 'title' => 'Mario Party', 'event_type' => 'party'), array('ID' => 4, 'title' => 'Duct Tape Party', 'event_type' => 'party'));# get a list of sort columns and their data to pass to array_multisort$sort = array();foreach($mylist as $k=>$v) { $sort['title'][$k] = $v['title']; $sort['event_type'][$k] = $v['event_type'];}# sort by event_type desc and then title ascarray_multisort($sort['event_type'], SORT_DESC, $sort['title'], SORT_ASC,$mylist);
As of PHP 5.5.0:
array_multisort(array_column($mylist, 'event_type'), SORT_DESC, array_column($mylist, 'title'), SORT_ASC, $mylist);
$mylist
is now:
array ( 0 => array ( 'ID' => 4, 'title' => 'Duct Tape Party', 'event_type' => 'party', ), 1 => array ( 'ID' => 3, 'title' => 'Mario Party', 'event_type' => 'party', ), 2 => array ( 'ID' => 1, 'title' => 'Boring Meeting', 'event_type' => 'meeting', ), 3 => array ( 'ID' => 2, 'title' => 'Find My Stapler', 'event_type' => 'meeting', ),)
PHP7 Makes sorting by multiple columns SUPER easy with the spaceship operator (<=>
) aka the "Combined Comparison Operator" or "Three-way Comparison Operator".
Resource: https://wiki.php.net/rfc/combined-comparison-operator
Sorting by multiple columns is as simple as writing balanced/relational arrays on both sides of the operator. Easy done!
When the $a
value is on the left of the spaceship operator and the $b
value is on the right, ASCending sorting is used.
When the $b
value is on the left of the spaceship operator and the $a
value is on the right, DESCending sorting is used.
When the spaceship operator compares two numeric strings, it compares them as numbers -- so you get natural sorting automagically.
I have not used uasort()
because I don't see any need to preserve the original indexes.
Code: (Demo) -- sorts by state
ASC, then event_type
ASC, then date_start
ASC
$array = [ ['ID' => 1, 'title' => 'Boring Meeting', 'date_start' => '2010-07-30', 'event_type' => 'meeting', 'state' => 'new-york'], ['ID' => 2, 'title' => 'Find My Stapler', 'date_start' => '2010-07-22', 'event_type' => 'meeting', 'state' => 'new-york'], ['ID' => 3, 'title' => 'Mario Party', 'date_start' => '2010-07-22', 'event_type' => 'party', 'state' => 'new-york'], ['ID' => 4, 'title' => 'Duct Tape Party', 'date_start' => '2010-07-28', 'event_type' => 'party', 'state' => 'california']];usort($array, function($a, $b) { return [$a['state'], $a['event_type'], $a['date_start']] <=> [$b['state'], $b['event_type'], $b['date_start']];});var_export($array);
Output
array ( 0 => array ( 'ID' => 4, 'title' => 'Duct Tape Party', 'date_start' => '2010-07-28', 'event_type' => 'party', 'state' => 'california', ), 1 => array ( 'ID' => 2, 'title' => 'Find My Stapler', 'date_start' => '2010-07-22', 'event_type' => 'meeting', 'state' => 'new-york', ), 2 => array ( 'ID' => 1, 'title' => 'Boring Meeting', 'date_start' => '2010-07-30', 'event_type' => 'meeting', 'state' => 'new-york', ), 3 => array ( 'ID' => 3, 'title' => 'Mario Party', 'date_start' => '2010-07-22', 'event_type' => 'party', 'state' => 'new-york', ),)
p.s. Arrow syntax with PHP7.4 and higher (Demo)...
usort($array, fn($a, $b) => [$a['state'], $a['event_type'], $a['date_start']] <=> [$b['state'], $b['event_type'], $b['date_start']]);
The equivalent technique with array_multisort()
and a call of array_column()
for every sorting criteria is: (Demo)
array_multisort( array_column($array, 'state'), array_column($array, 'event_type'), array_column($array, 'date_start'), $array);
You can do it with usort
. The $cmp_function
argument could be:
function my_sorter($a, $b) { $c = strcmp($a['state'], $b['state']); if($c != 0) { return $c; } $c = strcmp($a['event_type'], $b['event_type']); if($c != 0) { return $c; } return strcmp($a['date_start'], $b['date_start']);}
For an arbitrary number of fields in PHP 5.3, you can use closures to create a comparison function:
function make_cmp($fields, $fieldcmp='strcmp') { return function ($a, $b) use (&$fields) { foreach ($fields as $field) { $diff = $fieldcmp($a[$field], $b[$field]); if($diff != 0) { return $diff; } } return 0; }}usort($arr, make_cmp(array('state', 'event_type', 'date_start')))
For an arbitrary number of fields of different types in PHP 5.3:
function make_cmp($fields, $dfltcmp='strcmp') { # assign array in case $fields has no elements $fieldcmps = array(); # assign a comparison function to fields that aren't given one foreach ($fields as $field => $cmp) { if (is_int($field) && ! is_callable($cmp)) { $field = $cmp; $cmp = $dfltcmp; } $fieldcmps[$field] = $cmp; } return function ($a, $b) use (&$fieldcmps) { foreach ($fieldcmps as $field => $cmp) { $diff = call_user_func($cmp, $a[$field], $b[$field]); if($diff != 0) { return $diff; } } return 0; }}function numcmp($a, $b) { return $a - $b;}function datecmp($a, $b) { return strtotime($a) - strtotime($b);}/** * Higher priority come first; a priority of 2 comes before 1. */function make_evt_prio_cmp($priorities, $default_priority) { return function($a, $b) use (&$priorities) { if (isset($priorities[$a])) { $prio_a = $priorities[$a]; } else { $prio_a = $default_priority; } if (isset($priorities[$b])) { $prio_b = $priorities[$b]; } else { $prio_b = $default_priority; } return $prio_b - $prio_a; };}$event_priority_cmp = make_evt_prio_cmp( array('meeting' => 5, 'party' => 10, 'concert' => 7), 0);usort($arr, make_cmp(array('state', 'event' => $event_priority_cmp, 'date_start' => 'datecmp', 'id' => 'numcmp')))