PHP的不需要的行为

时间:2012-02-09 14:26:39

标签: php sorting usort

我遇到了PHP usort()的问题。让我们假设我有一个这样的数组(这是一个简化,我不使用名称,我有一个对象数组,而不是数组):

$data = array(
    array('name' => 'Albert',      'last' => 'Einstein'),
    array('name' => 'Lieserl',     'last' => 'Einstein'),
    array('name' => 'Alan',        'last' => 'Turing'  ),
    array('name' => 'Mileva',      'last' => 'Einstein'),
    array('name' => 'Hans Albert', 'last' => 'Einstein')
);

如您所见,数组是任意排序的。

现在,如果想按last对其进行排序,我会这样做:

function sort_some_people($a, $b) { return strcmp($a['last'], $b['last']); }
usort($data, 'sort_some_people');

我有:

Array (
    [0] => Array ( [name] => Mileva       [last] => Einstein )
    [3] => Array ( [name] => Albert       [last] => Einstein )
    [1] => Array ( [name] => Lieserl      [last] => Einstein )
    [2] => Array ( [name] => Hans Albert  [last] => Einstein )
    [4] => Array ( [name] => Alan         [last] => Turing   )
)

没关系,现在它们按last排序。但是,正如你所看到的,我刚刚完全失去了之前的排序。我在说什么呢?我希望像以前一样保留数组排序,但是作为辅助排序。我希望我很清楚。 实际我想使用usort()(因此,完全自定义排序)对数据进行排序,但如果两个项目之间的排序字段相同,我希望保持其相对位置就像以前一样。考虑到给定的示例,我希望Lieserl Einstein出现在Mileva Einstein之前,因为它在开始时就是这样。

4 个答案:

答案 0 :(得分:2)

PHP中使用的排序算法具有此属性,如果项匹配,则订单未定义。

如果您需要保留订单,则必须滚动自己的代码。

幸运的是,有人已经: http://www.php.net/manual/en/function.usort.php#38827

$data = array(
    array('name' => 'Albert',      'last' => 'Einstein'),
    array('name' => 'Lieserl',     'last' => 'Einstein'),
    array('name' => 'Alan',        'last' => 'Turing'  ),
    array('name' => 'Mileva',      'last' => 'Einstein'),
    array('name' => 'Hans Albert', 'last' => 'Einstein')
);

function sort_some_people($a, $b) {
        return strcmp($a['last'], $b['last']);
}

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

mergesort($data, 'sort_some_people');

print_r($data);

输出:

Array
(
    [0] => Array
        (
            [name] => Albert
            [last] => Einstein
        )

    [1] => Array
        (
            [name] => Lieserl
            [last] => Einstein
        )

    [2] => Array
        (
            [name] => Mileva
            [last] => Einstein
        )

    [3] => Array
        (
            [name] => Hans Albert
            [last] => Einstein
        )

    [4] => Array
        (
            [name] => Alan
            [last] => Turing
        )

)

瞧!

答案 1 :(得分:1)

$compare = strcmp($a['last'], $b['last']);
if ($compare == 0)
    $compare = strcmp($a['name'], $b['name']);
return $compare

答案 2 :(得分:1)

你要的是stable sorting algorithm,php没有提供。请注意,即使它有时看起来稳定,也不能保证它,并且在给定某些输入的情况下可能会出现行为异常。

如果您知道其他列的排序条件,则可以立即使用它们来获得所需的行为。 array_multisort这样做。以下是一种更强大的方法,因为比较逻辑完全是用户定义的。

// should behave similar to sql "order by last, first"
$comparatorSequence = array(
    function($a, $b) {
        return strcmp($a['last'], $b['last']);
    }
  , function($a, $b) {
        return strcmp($a['first'], $b['first']);
    }
  // more functions as needed
);

usort($theArray, function($a, $b) use ($comparatorSequence) {
    foreach ($comparatorSequence as $cmpFn) {
        $diff = call_user_func($cmpFn, $a, $b);
        if ($diff !== 0) {
            return $diff;
        }
    }
    return 0;
});

如果你真的需要一个稳定的排序,因为现有的元素顺序没有明确定义,请尝试编写自己的排序。例如,bubble sort非常容易编写。只要列表中的元素数量相对较少,这是一个很好的解决方案,否则就要实现其他稳定的排序算法之一。

答案 3 :(得分:0)

试试这个:

function sort_some_people($a, $b)
{
    $compareValue = 10 * strcmp($a['last'], $b['last']);
    $compareValue += 1 * strcmp($a['name'], $b['name']);
    return $compareValue;
}

示例:http://codepad.org/zkHviVBM

此函数使用十进制系统的每个数字作为排序条件。首先出现的数字最高。可能不是最聪明,但对我有用。