2012-08-28 5 views
7

Ich benutze usort, um ein Array mit einem assoziativen Array innerhalb jedes Elements zu sortieren.PHP usort reorders Array der Sortierwert ist für alle gleich

Wenn alle Werte, die ich im Array sortiere, gleich sind, ändert sich die Position der Elemente im Array. Gibt es eine Möglichkeit, dies zu verhindern?

Zum Beispiel diese:

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

Mai dazu geändert werden:

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

Dies ist die Sortierfunktion:

private function weightSortImplementation($a, $b){ 
    $aWeight = $a['autn_weight']; 
    $bWeight = $b['autn_weight']; 

    if ($aWeight == $bWeight) { 
     return 0; 
    } 
    return ($aWeight < $bWeight) ? 1 : -1; 
} 

ich überprüft haben, dass die weightSortImplementation Funktion immer gibt 0 zurück und zeigt damit, dass sie gleich sind. Also warum wird das Array immer noch neu geordnet?

+0

Das ist ein interessantes Thema. Ich habe das gerade getestet, und nach der Verwendung von 'usort' wurde die Reihenfolge umgekehrt. http://codepad.org/PRFpq8Ug –

+0

Sie dürfen keine [stable sort] (http://en.wikipedia.org/wiki/Sorting_algorithm#Stability) verwenden, die keine Garantien über die Reihenfolge der Elemente gibt, wenn sie es sind gleich. – JoeyJ

Antwort

11

Aha, ein Fall für die Schwartzian Transform.

Es besteht im Wesentlichen aus drei Schritten:

  1. Verzieren; Sie drehen jeden Wert in ein Array mit dem Wert als erstes Element und dem Schlüssel/Index als zweiten
  2. sortieren (wie normal)
  3. undecorate; Sie umkehren Schritt 1

Hier ist es (ich es auf Ihre speziellen Anwendungsfall gezwickt haben):

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'); 

Der Trick in der folgenden Behauptung:

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

Dies ist Was hält die richtige Reihenfolge Ihres Arrays. Und, keine Rekursion erforderlich :)

+0

super stuff bro .. !! – mithunsatheesh

+0

Hmm scheint, dass dies bei PHP 5.4 nicht funktioniert. –

+0

@JensKohl Haben Sie ein reproduzierbares Testskript, das ich mir ansehen könnte? –

8

From the documentation:

Wenn zwei Elemente gleich ist, ihre relative Reihenfolge im sortierten Feld undefiniert.

können Sie diese Funktion [source] verwenden, um im Falle von zwei Elementen bewahrt gleich:

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

Gibt es eine Möglichkeit, dies zu verhindern? Vielleicht mit anderen Sortiermethoden? oder wenn ich die Sortimplementierung ändere, könnte ich vielleicht die Gewichtungssortierung entweder 1 oder -1 erhalten, wenn sie gleich sind? – Chris

+0

Ich denke, du solltest deine Quelle angeben. Ich fand diese Methode [hier] dupliziert (http://stackoverflow.com/a/4353844/135101). –