2012-08-28 60 views
7

我使用usort排序与每个元素内的关联数组的数组。PHP usort reorders数组的排序值是相同的所有

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

例如这样的:

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表明它们是相同的。那么为什么这仍然是重新排列数组?

+0

这是一个有趣的问题。我刚刚测试过这个,在使用'usort'后,顺序被颠倒过来。 http://codepad.org/PRFpq8Ug –

+0

它们不得使用[稳定排序](http://en.wikipedia.org/wiki/Sorting_algorithm#Stability),如果它们是元素,它不会保证元素的顺序等于。 – JoeyJ

回答

11

啊哈,对于Schwartzian Transform的情况下。

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

  1. 装饰;您将每个值转换为数组,其中第一个元素的值为第一个元素,第二个元素的值为第二个
  2. undecorate;你反向第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) 

这是什么保持你的数组的正确顺序。并且,不需要递归:)

+0

超级东西哥.. – mithunsatheesh

+0

嗯似乎这不适合我在PHP 5.4上。 –

+0

@JensKohl你有可以重现的测试脚本吗? –

8

From the documentation

如果两个部件的比较结果为相等,则排序后的数组中它们的相对顺序是不确定的。

您可以使用此功能[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; 
} 
+0

有什么办法可以防止这种情况发生?也许使用不同的排序方法?或更改排序实现,我想我可以得到重量排序返回1或-1,如果他们是相同的? – Chris

+0

我认为你应该引用你的来源。我发现这种方法重复[这里](http://stackoverflow.com/a/4353844/135101)。 –