使用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
– 比较项目作为stringSORT_LOCALE_STRING
– 根据当前语言环境将项目作为string进行比较。 它使用语言环境,可以使用setlocale()
SORT_NATURAL
– 比较项目作为string使用“自然sorting”,如natsort()
SORT_FLAG_CASE
– 可以与SORT_STRING
或SORT_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))
就足够了(例如按升序排列)