2012-08-28 11 views
7

Sto usando usort per ordinare un array con un array associativo all'interno di ciascun elemento.PHP usort riordina array il valore di ordinamento è lo stesso per tutti

Quando tutti i valori che sto ordinando nell'array sono uguali allora cambia ancora la posizione degli elementi nella matrice, c'è un modo per impedirlo?

Per esempio, questo:

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

può essere modificato a questo:

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

Questa è la funzione di ordinamento:

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

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

Ho verificato che la funzione weightSortImplementation è sempre restituendo 0 mostrando che sono uguali. Quindi, perché questo è ancora riordinando la matrice?

+0

Questo è un problema interessante. Ho appena provato questo, e dopo aver usato 'usort' l'ordine è stato invertito. http://codepad.org/PRFpq8Ug –

+0

Non devono usare un [ordinamento stabile] (http://en.wikipedia.org/wiki/Sorting_algorithm#Stability), che non fornisce garanzie sull'ordine degli elementi se sono pari. – JoeyJ

risposta

11

Ah, un caso per l'Schwartzian Transform.

Essa consiste essenzialmente di tre fasi:

  1. decorare; si trasforma ogni valore in una matrice con il valore come primo elemento e la chiave/indice come il secondo
  2. ordinamento (come normale)
  3. undecorato; si inverte passaggio 1

Qui è (ho ottimizzato al vostro particolare caso d'uso):

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

Il trucco sta nel seguente asserzione:

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

Questo è ciò che mantiene l'ordine corretto del tuo array. E, nessuna ricorsione richiesta :)

+0

roba super bro .. !! – mithunsatheesh

+0

Hmm sembra che questo non funzioni per me su PHP 5.4. –

+0

@JensKohl Hai uno script di test riproducibile che potrei guardare? –

8

From the documentation:

Se due come uguali, il loro ordine relativo nell'array ordinato è indefinito.

È possibile utilizzare questa funzione [source] che conserva ordine nel caso di due elementi essendo uguale:

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

C'è un modo per impedirlo? Forse usando diversi metodi di smistamento? o cambiando l'implementazione di ordinamento, suppongo che potrei ottenere l'ordinamento del peso per restituire 1 o -1 se sono uguali? – Chris

+0

Penso che dovresti attribuire la tua fonte. Ho trovato questo metodo duplicato [qui] (http://stackoverflow.com/a/4353844/135101). –

Problemi correlati