2012-08-28 59 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)。 –