PHP usort重新排序数组-所有数组的排序值都是相同的


PHP usort reorders array the sort value is the same for all

我使用usort对每个元素中具有关联数组的数组进行排序。

当我在数组中排序的所有值都相同时,它仍然会更改数组中元素的位置,有办法防止这种情况吗?

例如:

array(
    array('name' => 'Ben', 'authn_weight' => 85.3),
    array('name' => 'Josh', 'authn_weight' => 85.3),
    array('name' => 'Fred', 'authn_weight' => 85.3)
);

可能更改为:

array(
    array('name' => 'Josh', 'authn_weight' => 85.3),
    array('name' => 'Ben', 'authn_weight' => 85.3),
    array('name' => 'Fred', 'authn_weight' => 85.3)
);

这是排序功能:

private function weightSortImplementation($a, $b){ 
    $aWeight = $a['autn_weight'];
    $bWeight = $b['autn_weight'];
    if ($aWeight == $bWeight) {
        return 0;
    }
    return ($aWeight < $bWeight) ? 1 : -1;
}

我已经检查过weightSortImplementation函数总是返回0,这表明它们是相同的。那么,为什么这仍然在重新排序数组呢?

Aha,Schwartzian变换的一个例子。

它基本上由三个步骤组成:

  1. 装饰;将每个值转换为一个数组,其中值为第一个元素,键/索引为第二个元素
  2. 排序(按常规)
  3. 不装饰;你反转步骤1

它在这里(我已经根据您的特定用例进行了调整):

function decorate(&$v, $k)
{
    $v['authn_weight'] = array($v['authn_weight'], $k);
}
function undecorate(&$v, $k)
{
    $v['authn_weight'] = $v['authn_weight'][0];
}
array_walk($a, 'decorate');
usort($a, 'weightSortImplementation');
array_walk($a, 'undecorate');

诀窍在于以下断言:

array($x, 0) < array($x, 1)

这就是保持数组正确顺序的原因。并且,不需要递归:)

来自文档:

如果两个成员比较为相等,则它们在排序数组中的相对顺序是未定义的。

你可以使用这个函数[source],在两个元素相等的情况下保持秩序:

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;
}