奇葩的usort

来源:互联网 发布:关于ipad的网络 编辑:程序博客网 时间:2024/04/28 00:47
奇葩的usort 

如果两个成员比较结果相同,则它们在排序后的数组中的顺序居然是未定义的

Note: 如果两个成员比较结果相同,则它们在排序后的数组中的顺序未经定义。到 PHP 4.0.6 之前,用户自定义函数将保留这些单元的原有顺序。但是由于在 4.1.0 中引进了新的排序算法,结果将不是这样了,因为对此没有一个有效的解决方案。 

如下例子:
我的本意是让$arr按key从大到小排序
例1:使用usort排序,结果key相等的元素位置受到影响
<?php
$arr = array(
    array('key' => 0,'name' => '4'),
    array('key' => 0,'name' => '5'),
    array('key' => 0,'name' => '2'),
    array('key' => 0,'name' => '3'),
    array('key' => 1,'name' => '1'),
);

usort($arr, 'cmp');
function cmp($a, $b){
    return ($a['key'] > $b['key']) ? -1 : 0;
}

var_export($arr);

输出为:
:!php test.php
array (
  0 => 
  array (
    'key' => 1,
    'name' => '1',
  ),
  1 => 
  array (
    'key' => 0,
    'name' => '3',
  ),
  2 => 
  array (
    'key' => 0,
    'name' => '2',
  ),
  3 => 
  array (
    'key' => 0,
    'name' => '5',
  ),
  4 => 
  array (
    'key' => 0,
    'name' => '4',
  ),
)

输出中key相等的元素的位置是随机的,但是,更加奇葩的是下面这个例子:
例2:当我把$arr中key=1的元素放在第1个位置时,神奇的事发生了:
<?php
$arr = array(
    array('key' => 1,'name' => '1'),
    array('key' => 0,'name' => '4'),
    array('key' => 0,'name' => '5'),
    array('key' => 0,'name' => '2'),
    array('key' => 0,'name' => '3'),
);

usort($arr, 'cmp');
function cmp($a, $b){
    return ($a['key'] > $b['key']) ? -1 : 0;
}

var_export($arr);

输出为:
:!php test.php
array (
  0 => 
  array (
    'key' => 0,
    'name' => '3',
  ),
  1 => 
  array (
    'key' => 0,
    'name' => '2',
  ),
  2 => 
  array (
    'key' => 0,
    'name' => '5',
  ),
  3 => 
  array (
    'key' => 0,
    'name' => '4',
  ),
  4 => 
  array (
    'key' => 1,
    'name' => '1',
  ),
)

不仅key相等的元素位置受到影响,而且最大的key对应的元素也没有排在首位,Oh My LadyGaga!暂行的解决方案是使用array_multisort来排序:此方法类似SQL中的Order By,先按key降序排序,然后再按原始索引排序

<?php
$arr = array(
    array('key' => 0,'name' => '4'),
    array('key' => 0,'name' => '5'),
    array('key' => 0,'name' => '2'),
    array('key' => 0,'name' => '3'),
    array('key' => 1,'name' => '1'),
);

$b = array();
$c = array();
$i = 0;
foreach($arr as $k => $v) {
    $b[$k] = $v['key'];
    $c[] = $i++;
}

array_multisort($b, SORT_NUMERIC, SORT_DESC, $c, SORT_ASC, $arr);

var_export($arr);

输出为:
:!php test.php
array (
  0 => 
  array (
    'key' => 1,
    'name' => '1',
  ),
  1 => 
  array (
    'key' => 0,
    'name' => '4',
  ),
  2 => 
  array (
    'key' => 0,
    'name' => '5',
  ),
  3 => 
  array (
    'key' => 0,
    'name' => '2',
  ),
  4 => 
  array (
    'key' => 0,
    'name' => '3',
  ),
)

当改变$arr中的元素位置时也不会影响到最终的排序结果。

Bingo!