|
uasort
Sort an array with a user-defined comparison function and maintain index association
(PHP 4, PHP 5)
Related Examples ( Source code ) » uasort Examples ( Source code ) » Using uasort() to Sort a Multdimensional Associative Array by One of Its Fields Code Examples / Notes » uasortnaholyr
You can sort a multidimensionnal array by any of its key with this function: function multi_sort($array, $key) { $cmp_val="((\$a['$key']>\$b['$key'])?1: ((\$a['$key']==\$b['$key'])?0:-1))"; $cmp=create_function('$a, $b', "return $body;"); uasort($array, $cmp); return $array; } example: $myarray = array( array("name"=>"kernighan", "language"=>"c"), array("name"=>"lerdorf", "language"=>"php"), array("name"=>"Stroustrup", "language"=>"c++"), array("name"=>"Gosling", "language"=>"java") ); multi_sort($myarray, "name") returns: name=Gosling language=java name=Kernighan language=c name=Lerdorf language=php name=Stroustrup language=c++ david __at__ castlelaing.com
WARNING:-Regarding remmy.cjb.net (22-Oct-2003 05:57) mutisort() function: Sorting by floating point numbers doesn't work in the current function. Use the modified version below if you want to sort by a floating point column. <?php // Based on the other notes given before. // Sorts an array (you know the kind) by key // and by the comparison operator you prefer. // Note that instead of most important criteron first, it's // least important criterion first. // The default sort order is ascending, and the default sort // type is strnatcmp. // function multisort($array[, $key, $order, $type]...) function multisort($array) { for($i = 1; $i < func_num_args(); $i += 3) { $key = func_get_arg($i); if (is_string($key)) $key = '"'.$key.'"'; $order = true; if($i + 1 < func_num_args()) $order = func_get_arg($i + 1); $type = 0; if($i + 2 < func_num_args()) $type = func_get_arg($i + 2); switch($type) { case 1: // Case insensitive natural. $t = 'strcasecmp($a[' . $key . '], $b[' . $key . '])'; break; case 2: // Numeric. $t = '($a[' . $key . '] == $b[' . $key . ']) ? 0:(($a[' . $key . '] < $b[' . $key . ']) ? -1 : 1)'; break; case 3: // Case sensitive string. $t = 'strcmp($a[' . $key . '], $b[' . $key . '])'; break; case 4: // Case insensitive string. $t = 'strcasecmp($a[' . $key . '], $b[' . $key . '])'; break; default: // Case sensitive natural. $t = 'strnatcmp($a[' . $key . '], $b[' . $key . '])'; break; } echo $t; usort($array, create_function('$a, $b', '; return ' . ($order ? '' : '-') . '(' . $t . ');')); } return $array; } ?> joeseed86
Using uasort to alphabetically sort nested objects: In this example, a "collection" object contains an array of "dataItem" objects which consist of a string name, a string attribute x and an arbitrary integer y. This code allows you to sort the dataset by any of the dataItem attributes, or the order in which they were originally added to the set. I'm using PHP 4.23 at work at the moment for legacy reasons, so I wrote the example following that object model. <?php class dataItem { var $name; var $x; //Constructor function dataItem($name,$x,$y) { $this->name = $name; $this->x = $x; $this->y = $y; } } class collection { var $dataSet = array(); //Creates a new data item and adds it to our array function add($name,$x,$y) { $this->dataSet[] = new dataItem($name,$x,$y); } //The wrapper sort function function sortDataSet($s) { //Sort by the given parameter switch($s) { case "name": //Note use of array to reference member method of this object in callback uasort($this->dataSet,array($this,"cmpName")); break; case "x": uasort($this->dataSet,array($this,"cmpX")); break; case "y": uasort($this->dataSet,array($this,"cmpY")); break; case "added": default: //Re-sort array by original keys ksort($this->dataSet); } } //Callback function for sorting by name //$a and $b are dataItem objects function cmpName($a,$b) { //Use sort() for simple alphabetical comparison //Convert to lowercase to ensure consistent behaviour $sortable = array(strtolower($a->name),strtolower($b->name)); $sorted = $sortable; sort($sorted); //If the names have switched position, return -1. Otherwise, return 1. return ($sorted[0] == $sortable[0]) ? -1 : 1; } //Callback function for sorting by x //$a and $b are dataItem objects function cmpX($a,$b) { //Use sort() for simple alphabetical comparison //Convert to lowercase to ensure consistent behaviour $sortable = array(strtolower($a->x),strtolower($b->x)); $sorted = $sortable; sort($sorted); //If the names have switched position, return -1. Otherwise, return 1. return ($sorted[0] == $sortable[0]) ? -1 : 1; } //Callback function for sorting by y //$a and $b are dataItem objects function cmpY($a,$b) { //If $a's y attribute >= $b's y attribute, return 1. Otherwise, return -1. return ($a->y >= $b->y) ? 1 : -1; } } //Create a collection object $myCollection = new collection(); //Add a few "records" $myCollection->add("pancake","egg",7); $myCollection->add("France","Paris",2); $myCollection->add("Naked Gun","Leslie Nielsen",1); $myCollection->add("OSX","huge icons",33); $myCollection->add("telephone","keypad",-3); //Test the output //Sort by name: $myCollection->sortDataSet("name"); print_r($myCollection->dataSet); //Sort by x $myCollection->sortDataSet("x"); print_r($myCollection->dataSet); //Sort by y $myCollection->sortDataSet("y"); print_r($myCollection->dataSet); //Sort by order added $myCollection->sortDataSet("added"); print_r($myCollection->dataSet); ?> Will give this output (Anotated and re-formatted for ease of reading): Sorted by name: France Paris 2 Naked Gun Leslie Nielsen 1 OSX huge icons 33 pancake egg 7 telephone keypad -3 Sorted by x: pancake egg 7 OSX huge icons 33 telephone keypad -3 Naked Gun Leslie Nielsen 1 France Paris 2 Sorted by y: telephone keypad -3 Naked Gun Leslie Nielsen 1 France Paris 2 pancake egg 7 OSX huge icons 33 Sorted as added: pancake egg 7 France Paris 2 Naked Gun Leslie Nielsen 1 OSX huge icons 33 telephone keypad -3 This is obviously a trivial case, but it can be very useful for larger data structures. stilgar_cpsnospam
Use example: $array[0]['Fator1']=7; $array[0]['Fator2']="Name"; $array[1]['Fator1']=5; $array[1]['Fator2']="Name"; $array[2]['Fator1']=7; $array[2]['Fator2']="NameDiferente"; ..... We want to order by Fator1, then Fator2, then: function Compare($ar1, $ar2) { if ($ar1['Fator1']<$ar2['Fator1']) return -1; else if ($ar1['Fator1']>$ar2['Fator1']) return 1; if ($ar1['Fator2']<$ar2['Fator2']) return -1; else if ($ar1['Fator2']>$ar2['Fator2']) return 1; return 0; } To sort now, we use: uasort($array, 'Compare'); lucas dot karisny
The following is a modification of the dholme/messju masort func using david's float code with automatic data type detection. As long as the value isn't a string numeral (I.E. "1", '13.4') this should sort strings and numbers without having to explicity set which they are. <?php function masort(&$data, $sortby) { static $sort_funcs = array(); if (empty($sort_funcs[$sortby])) { $code = "\$c=0;"; foreach (split(',', $sortby) as $key) { $array = array_pop($data); array_push($data, $array); if(is_numeric($array[$key])) $code .= "if ( \$c = ((\$a['$key'] == \$b['$key']) ? 0:((\$a['$key'] < \$b['$key']) ? -1 : 1 )) );"; else $code .= "if ( (\$c = strcasecmp(\$a['$key'],\$b['$key'])) != 0 ) return \$c;\n"; } $code .= 'return $c;'; $sort_func = $sort_funcs[$sortby] = create_function('$a, $b', $code); } else { $sort_func = $sort_funcs[$sortby]; } $sort_func = $sort_funcs[$sortby]; uasort($data, $sort_func); } ?> dn dot php
regarding remmy.cjb.net (22-Oct-2003 05:57) note: The "multisort" function is not working. Try the following example. ( I hope this and your note will be deleted soon.) ..- Denis <?php $a = array( array('c1' => 1, 'c2' => 1, 'c3' => 1, 'c4' => 1), array('c1' => 1, 'c2' => 1, 'c3' => 1, 'c4' => 2), array('c1' => 1, 'c2' => 1, 'c3' => 2, 'c4' => 1), array('c1' => 1, 'c2' => 1, 'c3' => 2, 'c4' => 2) ); echo('<pre>'); print_r(multisort($a, "'c4'", true, 2,"'c3'", true, 2,"'c2'", true, 2,"'c1'", true, 2)); echo('</pre>'); ?> php
Is it just me, or are the examples below misleading, and actually demonstrating situations that would be more appropriate for usort()? After trying to make sense of the uasort() description, it sounds like it's more for sorting a 1D array like this: "john" => "$23.12" "tim" => "$6.50" "bob" => "$18.54" and getting back: "tim" => "$6.50" "bob" => "$18.54" "john" => $23.12" (assuming, of course, that your sort function is lopping off the $ and evaluating as a number -- which would complicate the use of asort() ;) remmy.cjb.net
Hope this helps! - Remmy <?php // Based on the other notes given before. // Sorts an array (you know the kind) by key // and by the comparison operator you prefer. // Note that instead of most important criteron first, it's // least important criterion first. // The default sort order is ascending, and the default sort // type is strnatcmp. // function multisort($array[, $key, $order, $type]...) function multisort($array) { for($i = 1; $i < func_num_args(); $i += 3) { $key = func_get_arg($i); $order = true; if($i + 1 < func_num_args()) $order = func_get_arg($i + 1); $type = 0; if($i + 2 < func_num_args()) $type = func_get_arg($i + 2); switch($type) { case 1: // Case insensitive natural. $t = 'strcasenatcmp($a[' . $key . '], $b[' . $key . '])'; break; case 2: // Numeric. $t = '$a[' . $key . '] - $b[' . $key . ']'; break; case 3: // Case sensitive string. $t = 'strcmp($a[' . $key . '], $b[' . $key . '])'; break; case 4: // Case insensitive string. $t = 'strcasecmp($a[' . $key . '], $b[' . $key . '])'; break; default: // Case sensitive natural. $t = 'strnatcmp($a[' . $key . '], $b[' . $key . '])'; break; } uasort($array, create_function('$a, $b', 'return ' . ($order ? '' : '-') . '(' . $t . ');')); } return $array; } $a = array( array('id' => 1, 'name' => 'apple'), array('id' => 2, 'name' => 'orange'), array('id' => 8, 'name' => 'banana'), array('id' => 8, 'name' => 'grapefruit'), array('id' => 9, 'name' => 'smoke'), array('id' => 1, 'name' => 'screen') ); // This works like MYSQL 'ORDER BY id DESC, name ASC' // Note the quoting of string literal keys. echo('<pre>'); print_r(multisort($a, "'name'", true, 0, "'id'", false, 2)); echo('</pre>'); ?> dholmes
Here is a little sort function that actually uses a dynamic callback for usort to do it's thing. It assumes your data is in the form of: $data = array( array('ID'=>'6','LAST'=>'Holmes','FIRST'=>'Dan'), array('ID'=>'1234','LAST'=>'Smith','FIRST'=>'Agent K'), array('ID'=>'2','LAST'=>'Smith','FIRST'=>'Agent J'), array('ID'=>'4','LAST'=>'Barney','FIRST'=>'Bob')); Now, you want to sort on one or more cols, don't you? masort($data, 'LAST,FIRST'); or masort($data,array('FIRST','ID')); Of course you could add a bunch to it (like numeric comparison if appropriate, desc/asc, etc) but it works for me. function masort(&$data, $sortby){ if(is_array($sortby)){ $sortby = join(',',$sortby); } uasort($data,create_function('$a,$b','$skeys = split(\',\',\''.$sortby.'\'); foreach($skeys as $key){ if( ($c = strcasecmp($a[$key],$b[$key])) != 0 ){ return($c); } } return($c); ')); } Notice that I am splitting the string in the comparison function? While this is certainly slower, it was the only way I would find to "pass" and "array". If anyone has a better way, please suggest. Then inside, we (string) compare the values only moving to the next key if the values are the same...and so on, and so on. siefer
Hello naholyr at yahoo dot fr! should it be $cmp = create_function('$a, $b', "return $cmp_val;"); ? this works with my arrays ;-) regards, Christopher cablehead
dholmes we turned your masort function into a smarty plugin: http://www.phpinsider.com/smarty-forum/viewtopic.php?t=1079 messju contributed the following performance improvements. function masort(&$data, $sortby) { static $sort_funcs = array(); if (empty($sort_funcs[$sortby])) { $code = "\$c=0;"; foreach (split(',', $sortby) as $key) { $code .= "if ( (\$c = strcasecmp(\$a['$key'],\$b['$key'])) != 0 ) return \$c;\n"; } $code .= 'return $c;'; $sort_func = $sort_funcs[$sortby] = create_function('$a, $b', $code); } else { $sort_func = $sort_funcs[$sortby]; } $sort_func = $sort_funcs[$sortby]; uasort($data, $sort_func); } thank you for the cool function! marek
Below another array sorting function - you can use many keys, whether order type is ascendant or descendant, if values of given key should be treat as string or numeric, if array keys should be preserved. This function is locale-safe - it means that string sorting will be based on setLocale settings. You should be aware that I did not make comprehensive tests, so be careful... // my locales SetLocale(LC_COLLATE,"pl_PL.UTF-8"); SetLocale(LC_CTYPE, "pl_PL.UTF-8"); function Array_Sort ($array, $arguments = Array(), $keys = true) { // comparing function code $code = ""; // foreach sorting argument (array key) foreach ($arguments as $argument) { // order field $field = substr($argument, 2, strlen($argument)); // sort type ("s" -> string, "n" -> numeric) $type = $argument[0]; // sort order ("+" -> "ASC", "-" -> "DESC") $order = $argument[1]; // add "if" statement, which checks if this argument should be used $code .= "if (!Is_Numeric(\$result) || \$result == 0) "; // if "numeric" sort type if (strtolower($type) == "n") $code .= $order == "-" ? "\$result = (\$a['{$field}'] > \$b['{$field}'] ? -1 : (\$a['{$field}'] < \$b['{$field}'] ? 1 : 0));" : "\$result = (\$a['{$field}'] > \$b['{$field}'] ? 1 : (\$a['{$field}'] < \$b['{$field}'] ? -1 : 0));"; // if "string" sort type else $code .= $order == "-" ? "\$result = strcoll(\$a['{$field}'], \$b['{$field}']) * -1;" : "\$result = strcoll(\$a['{$field}'], \$b['{$field}']);"; } // return result $code .= "return \$result;"; // create comparing function $compare = create_function('$a, $b', $code); // sort array and preserve keys if ($keys) uasort($array, $compare); // sort array, but not preserve keys else usort($array, $compare); // return array return $array; } Example array: $array['sdsd'] = array("dir" => 1, "name" => "sas", "olek" => "sdsd"); $array['sds2'] = array("dir" => 2, "name" => "śas", "olek" => "t"); Example - preserve keys: print_r(Array_Sort($array, Array("s-name", "n-dir", "s+olek"))); Array ( [sds2] => Array ( [dir] => 1 [name] => śas [olek] => t ) [sdsd] => Array ( [dir] => 1 [name] => sas [olek] => sdsd ) ) Example - without preserving keys: print_r(Array_Sort($array, Array("s-name", "n-dir", "s+olek")), false); Array ( [0] => Array ( [dir] => 1 [name] => śas [olek] => t ) [1] => Array ( [dir] => 1 [name] => sas [olek] => sdsd ) ) Enyoj rlynch
A subtle bug, corrected... <?php function masort(&$data, $sortby) { static $sort_funcs = array(); if (empty($sort_funcs[$sortby])) { $code = "\$c=0;"; foreach (split(',', $sortby) as $key) { $array = array_pop($data); array_push($data, $array); if(is_numeric($array[$key])) $code .= "if ( \$c = ((\$a['$key'] == \$b['$key']) ? 0:((\$a['$key'] < \$b['$key']) ? -1 : 1 )) ) return \$c;"; else $code .= "if ( (\$c = strcasecmp(\$a['$key'],\$b['$key'])) != 0 ) return \$c;\n"; } $code .= 'return $c;'; $sort_func = $sort_funcs[$sortby] = create_function('$a, $b', $code); } else { $sort_func = $sort_funcs[$sortby]; } $sort_func = $sort_funcs[$sortby]; uasort($data, $sort_func); } ?> Note that $code .= "if ( \$c = ((\$a['$key'] == \$b['$key']) ? 0:((\$a['$key'] < \$b['$key']) ? -1 : 1 )) ) return \$c;"; Has had the "return \$c" added. Ultimately what the method is trying to accomplish is to build a chain of sort-order precedence. But this requires each evaluation to short-circuit out with a return. It was missing. Peace |
Change Languagearray_change_key_case array_chunk array_combine array_count_values array_diff_assoc array_diff_key array_diff_uassoc array_diff_ukey array_diff array_fill_keys array_fill array_filter array_flip array_intersect_assoc array_intersect_key array_intersect_uassoc array_intersect_ukey array_intersect array_key_exists array_keys array_map array_merge_recursive array_merge array_multisort array_pad array_pop array_product array_push array_rand array_reduce array_reverse array_search array_shift array_slice array_splice array_sum array_udiff_assoc array_udiff_uassoc array_udiff array_uintersect_assoc array_uintersect_uassoc array_uintersect array_unique array_unshift array_values array_walk_recursive array_walk array arsort asort compact count current each end extract in_array key krsort ksort list natcasesort natsort next pos prev range reset rsort shuffle sizeof sort uasort uksort usort |