note 104398 modified in function.usort by danbrown

From: Date: Mon, 13 Jun 2011 17:47:13 +0000
Subject: note 104398 modified in function.usort by danbrown
References: 1  Groups: php.notes 
Request: Send a blank email to php-notes+get-180489@lists.php.net to get a copy of this message
[The second version of a function I wrote], just a little cleaner to use, since the usort call is now folded into the custom 'usortByArrayKey' function. Seems like this is a popular problem - sorting an array of arrays by the keys of sub-arrays, so I'll go ahead and throw in an approach I haven't seen anyone mention yet. It only works since PHP 5.3 because that's when anonymous functions got added to the language. <?php function usortByArrayKey(&$array, $key, $asc=SORT_ASC) { $sort_flags = array(SORT_ASC, SORT_DESC); if(!in_array($asc, $sort_flags)) throw new InvalidArgumentException('sort flag only accepts SORT_ASC or SORT_DESC'); $cmp = function(array $a, array $b) use ($key, $asc, $sort_flags) { if(!is_array($key)) { //just one key and sort direction if(!isset($a[$key]) || !isset($b[$key])) { throw new Exception('attempting to sort on non-existent keys'); } if($a[$key] == $b[$key]) return 0; return ($asc==SORT_ASC xor $a[$key] < $b[$key]) ? 1 : -1; } else { //using multiple keys for sort and sub-sort foreach($key as $sub_key => $sub_asc) { //array can come as 'sort_key'=>SORT_ASC|SORT_DESC or just 'sort_key', so need to detect which if(!in_array($sub_asc, $sort_flags)) { $sub_key = $sub_asc; $sub_asc = $asc; } //just like above, except 'continue' in place of return 0 if(!isset($a[$sub_key]) || !isset($b[$sub_key])) { throw new Exception('attempting to sort on non-existent keys'); } if($a[$sub_key] == $b[$sub_key]) continue; return ($sub_asc==SORT_ASC xor $a[$sub_key] < $b[$sub_key]) ? 1 : -1; } return 0; } }; usort($array, $cmp); }; ?> Examples: 1) Sort a multi-dimensional array by a single key, from least to greatest: usortByArrayKey($resultset, 'order_id'); 2) Sort a multi-dimensional array by a single key, from greatest to least: usortByArrayKey($resultset, 'purchase_amount', SORT_DESC); 3) Sort a multi-dimensional array first by one key, but then by a different key when the first one is equal, both from least to greatest: usortByArrayKey($resultset, array('product_id', 'price')); 4) Sort a multi-dimensional array by two different keys from least to greatest, and then a third from greatest to least if the first two keys map to the equal values: usortByArrayKey($resultset, array('product_id', 'customer_id', 'date'=>SORT_DESC)); 5) Sort a multi-dimensional array by multiple keys, all from greatest to least: usortByArrayKey($resultset, array('product_id', 'price'), SORT_DESC); One word of caution, since SORT_ASC and SORT_DESC have actual int values of 4 and 3, respectively, if you need to sort a multi-dimensional array by keys 3 or 4, and you're using an array to pass in your keys, you MUST specify your sort flags (e.g. array(3 => SORT_ASC,...)) to prevent unexpected behavior, instead of relying on the function's second parameter as your default sort flag. --was-- Pretty much the same thing as my previous note, just a little cleaner to use, since the usort call is now folded into the custom 'usortByArrayKey' function. <?php function usortByArrayKey(&$array, $key, $asc=SORT_ASC) { $sort_flags = array(SORT_ASC, SORT_DESC); if(!in_array($asc, $sort_flags)) throw new InvalidArgumentException('sort flag only accepts SORT_ASC or SORT_DESC'); $cmp = function(array $a, array $b) use ($key, $asc, $sort_flags) { if(!is_array($key)) { //just one key and sort direction if(!isset($a[$key]) || !isset($b[$key])) { throw new Exception('attempting to sort on non-existent keys'); } if($a[$key] == $b[$key]) return 0; return ($asc==SORT_ASC xor $a[$key] < $b[$key]) ? 1 : -1; } else { //using multiple keys for sort and sub-sort foreach($key as $sub_key => $sub_asc) { //array can come as 'sort_key'=>SORT_ASC|SORT_DESC or just 'sort_key', so need to detect which if(!in_array($sub_asc, $sort_flags)) { $sub_key = $sub_asc; $sub_asc = $asc; } //just like above, except 'continue' in place of return 0 if(!isset($a[$sub_key]) || !isset($b[$sub_key])) { throw new Exception('attempting to sort on non-existent keys'); } if($a[$sub_key] == $b[$sub_key]) continue; return ($sub_asc==SORT_ASC xor $a[$sub_key] < $b[$sub_key]) ? 1 : -1; } return 0; } }; usort($array, $cmp); }; ?> Examples: 1) Sort a multi-dimensional array by a single key, from least to greatest: usortByArrayKey($resultset, 'order_id'); 2) Sort a multi-dimensional array by a single key, from greatest to least: usortByArrayKey($resultset, 'purchase_amount', SORT_DESC); 3) Sort a multi-dimensional array first by one key, but then by a different key when the first one is equal, both from least to greatest: usortByArrayKey($resultset, array('product_id', 'price')); 4) Sort a multi-dimensional array by two different keys from least to greatest, and then a third from greatest to least if the first two keys map to the equal values: usortByArrayKey($resultset, array('product_id', 'customer_id', 'date'=>SORT_DESC)); 5) Sort a multi-dimensional array by multiple keys, all from greatest to least: usortByArrayKey($resultset, array('product_id', 'price'), SORT_DESC); http://php.net/manual/en/function.usort.php

« previous php.notes (#180489) next »