使用PHP的uasort进行sorting时保留键顺序(稳定sorting)

这个问题实际上是从这里的另一个启发,我想扩大一点。

在PHP中有一个关联数组可以对其值进行sorting,但是在哪里使用一个(或多个)PHP的内置sorting函数来保存原始键顺序?

这是我用来testing可能的解决scheme的脚本(没有find任何):

<?php header('Content-type: text/plain'); for($i=0;$i<10;$i++){ $arr['key-'.$i] = rand(1,5)*10; } uasort($arr, function($a, $b){ // sort condition may go here // // Tried: return ($a == $b)?1:($a - $b); // // Tried: return $a >= $b; // }); print_r($arr); ?> 

陷阱 :因为按键是在原始数组中sorting的,请不要试图通过按键进行sorting来恢复到原来的顺序。 我做了一个例子,让他们更容易在输出中直观地检查它们的顺序。

感谢您的testinginput,
阿林

由于PHP 4.1.0之后不支持稳定sorting ,因此您需要编写自己的函数。

这似乎是做你在问什么: http : //www.php.net/manual/en/function.usort.php#38827

正如手册所说:“如果两个成员比较相等,则它们在已sorting数组中的顺序是未定义的。” 这意味着使用的sorting不是“稳定的”,并且可能会改变比较相等的元素的顺序。

有时你确实需要一个稳定的sorting。 例如,如果您按一个字段sorting列表,然后再按另一个字段sorting,但不希望丢失前一个字段的sorting。 在这种情况下,最好使用带有两个字段的比较函数的usort,但如果不能这样做,则使用下面的函数。 这是一个合并sorting,保证了O(n * log(n))的复杂性,这意味着即使使用更大的列表(不像bubblesort和插入sorting,它们是O(n ^ 2)),它仍然保持相当快的速度。

 <?php function mergesort(&$array, $cmp_function = 'strcmp') { // Arrays of size < 2 require no action. if (count($array) < 2) return; // Split the array in half $halfway = count($array) / 2; $array1 = array_slice($array, 0, $halfway); $array2 = array_slice($array, $halfway); // Recurse to sort the two halves mergesort($array1, $cmp_function); mergesort($array2, $cmp_function); // If all of $array1 is <= all of $array2, just append them. if (call_user_func($cmp_function, end($array1), $array2[0]) < 1) { $array = array_merge($array1, $array2); return; } // Merge the two sorted arrays into a single sorted array $array = array(); $ptr1 = $ptr2 = 0; while ($ptr1 < count($array1) && $ptr2 < count($array2)) { if (call_user_func($cmp_function, $array1[$ptr1], $array2[$ptr2]) < 1) { $array[] = $array1[$ptr1++]; } else { $array[] = $array2[$ptr2++]; } } // Merge the remainder while ($ptr1 < count($array1)) $array[] = $array1[$ptr1++]; while ($ptr2 < count($array2)) $array[] = $array2[$ptr2++]; return; } ?> 

此外,你可能会发现这个论坛线程有趣。

array_multisort派上用场,只需要使用一个有序的范围作为第二个数组( $order只是临时的,它用来按照原始顺序排列第一个数组的等价项):

 $a = [ "key-0" => 5, "key-99" => 3, "key-2" => 3, "key-3" => 7 ]; $order = range(1,count($a)); array_multisort($a, SORT_ASC, $order, SORT_ASC); var_dump($a); 

产量

 array(4) { ["key-99"]=> int(3) ["key-2"]=> int(3) ["key-0"]=> int(5) ["key-3"]=> int(7) } 

我用未经过sorting的键来使用testing数据来certificate它正常工作。 尽pipe如此,这里是你的testing脚本的输出:

 Array ( [key-1] => 10 [key-4] => 10 [key-5] => 20 [key-8] => 20 [key-6] => 30 [key-9] => 30 [key-2] => 40 [key-0] => 50 [key-3] => 50 [key-7] => 50 ) 

下行

它只适用于预定义的比较,你不能使用自己的比较function。 可能的值( array_multisort()第二个参数)是:

sortingtypes标志

  • SORT_ASC – 对项目进行递增sorting。
  • SORT_DESC – 递减sorting项目。
  • SORT_REGULAR – 通常比较项目(不要更改types)
  • SORT_NUMERIC – 用数字比较项目
  • SORT_STRING – 比较项目作为string
  • SORT_LOCALE_STRING – 根据当前语言环境将项目作为string进行比较。 它使用语言环境,可以使用setlocale()
  • SORT_NATURAL – 比较项目作为string使用“自然sorting”,如natsort()
  • SORT_FLAG_CASE – 可以与SORT_STRINGSORT_NATURAL组合(按位或)对string进行不区分大小写的sorting

为了将来的参考,我已经在Github上提供了一组内置PHP函数的稳定sorting变体: https : //github.com/vanderlee/PHP-stable-sort-functions ,基于@ Jack的解决scheme和其他一些技巧。

为了完整起见,您还应该查看Schwartzian变换 :

 // decorate step $key = 0; foreach ($arr as &$item) { $item = array($item, $key++); // add array index as secondary sort key } // sort step asort($arr); // sort it // undecorate step foreach ($arr as &$item) { $item = $item[0]; // remove decoration from previous step } 

PHP的默认sortingalgorithm对数组工作正常,因为:

 array(1, 0) < array(2, 0); // true array(1, 1) < array(1, 2); // true 

如果你想使用你自己的sorting标准,你也可以使用uasort()

 // each parameter is an array with two elements // [0] - the original item // [1] - the array key function mysort($a, $b) { if ($a[0] != $b[0]) { return $a[0] < $b[0] ? -1 : 1; } else { // $a[0] == $b[0], sort on key return $a[1] < $b[1] ? -1 : 1; // ASC } } 

这是一个解决scheme,使用它你可以在usortfunction中实现稳定的sorting

 public function sortBy(array &$array, $value_compare_func) { $index = 0; foreach ($array as &$item) { $item = array($index++, $item); } $result = usort($array, function($a, $b) use ($value_compare_func) { $result = call_user_func($value_compare_func, $a[1], $b[1]); return $result == 0 ? $a[0] - $b[0] : $result; }); foreach ($array as &$item) { $item = $item[1]; } return $result; } 

只是用一些非常具体的案例来完成答复。 如果$array的数组键是默认的数组键,那么一个简单的array_values(asort($array))就足够了(例如按升序排列)