问题说明

开发时遇到一个问题,一个根据权重排序的人员列表,每次刷新,两个人就调换一下位置。查原因时,发现了PHP中关于排序的一个诡异问题。当对数组进行保持索引的键排序时,如果两个值相同,那么索引的位置并不会保持原先的顺序,而是调换,或者很诡异。

思考过程

看下面的一个例子

<?php
$arr = [
    'a' => 1,
    'c' => 3,
    'd' => 3,
    'e' => 3,
    'f' => 4,
    'g' => 5
];
asort($arr);
print_r($arr);
// 输出结果
Array
(
    [a] => 1
    [e] => 3
    [d] => 3
    [c] => 3
    [f] => 4
    [g] => 5
)

这里,值为3的元素,键的顺序被调换了。这也就是我之前问题的根本,当刷新时,根据权重排序,如果两个人权重相同,则进行了调换。不知道为啥子会调换,值相同时,不应该保持原来的顺序吗?想到这,我想起PHP有其他的排序函数,允许自定义排序方式,尝试了一下

<?php
$arr = [
    'a' => 1,
    'c' => 3,
    'd' => 3,
    'e' => 3,
    'f' => 4,
    'g' => 5
];
uasort($arr, function ($a, $b) {
    if ($a === $b) return 0;
    return $a < $b ?-1:1;
});
print_r($arr);
// 输出结果
Array
(
    [a] => 1
    [e] => 3
    [d] => 3
    [c] => 3
    [f] => 4
    [g] => 5
)

这就不明白了。使用自定义函数时,处理方式也是如此。十分好奇PHP内部是怎么处理的,尝试的看了一下源码,没看明白。所以,这个问题暂时到这里,后面会慢慢学习一下。

但是,怎么解决这个问题呢?当时有点绕进去了,脑袋不够灵活。总是想着,既然,相同元素被调换位置了,那我怎么不让他变。自己写补丁,折腾好久。最后,灵机一动,解决的很粗暴

<?php
<?php
$arr = [
    'a' => 1,
    'c' => 3,
    'd' => 3,
    'e' => 3,
    'f' => 4,
    'g' => 5
];
asort($arr);
asort($arr);
print_r($arr);
// 输出结果
Array
(
    [a] => 1
    [c] => 3
    [d] => 3
    [e] => 3
    [f] => 4
    [g] => 5
)

这么干,是因为我觉得相同元素,他也进行了调换,那索性再排序一下,负负得正吧。然后就解决了。

我也以为问题解决了。然后,我想,如果数组中有很多值相同的元素,这个方法是不是也有效?然后发现了更多诡异问题。首先是,如果键名不是上述的a-e,而是如下情况

<?php
$arr = [
    'a1' => 1,
    'b1' => 2,
    'c1' => 3,
    'c2' => 3,
    'c3' => 3,
    'd1' => 4,
];
asort($arr);
print_r($arr);
// 输出结果
Array
(
    [a1] => 1
    [b1] => 2
    [c3] => 3
    [c1] => 3
    [c2] => 3
    [d1] => 4
)

发现,这个时候,值相同的键,并不是跟着排序了。而是一种看不懂的方式。

如果,一个数组包含多个值相同的呢?

<?php
$arr = [
    'a' => 1,
    'b' => 2,
    'c' => 3,
    'd' => 3,
    'e' => 3,
    'f' => 4,
    'm' => 2,
    'n' => 2
];
asort($arr);
print_r($arr);
// 输出结果
Array
(
    [a] => 1
    [n] => 2
    [b] => 2
    [m] => 2
    [d] => 3
    [c] => 3
    [e] => 3
    [f] => 4
)

这就看不懂了。所以,我觉得,想了解整个过程是如何的,还是得去阅读以下源码是怎么写的。