2009-05-01 5 views
6

Sto cercando di ordinare una matrice di matrici multidimensionali su più di una colonna. Per complicarlo ulteriormente mi piacerebbe essere in grado di impostare specifiche opzioni di ordinamento per chiave/colonna. Ho ciò che è simile al risultato di una query DB, ma in realtà non proviene da uno, quindi la necessità di ordinarlo in PHP piuttosto che SQL.Ordina array di matrici multi-dimensionali su più di una "colonna" (chiave) con opzioni di ordinamento specificate

Array 
(
    [0] => Array 
     (
      [first_name] => Homer 
      [last_name] => Simpson 
      [city] => Springfield 
      [state] => Unknown 
      [zip] => 66735 
     ) 

    [1] => Array 
     (
      [first_name] => Patty 
      [last_name] => Bouvier 
      [city] => Scottsdale 
      [state] => Arizona 
      [zip] => 85250 
     ) 

    [2] => Array 
     (
      [first_name] => Moe 
      [last_name] => Szyslak 
      [city] => Scottsdale 
      [state] => Arizona 
      [zip] => 85255 
     ) 

    [3] => Array 
     (
      [first_name] => Nick 
      [last_name] => Riviera 
      [city] => Scottsdale 
      [state] => Arizona 
      [zip] => 85255 
     ) 

) 

Vorrei essere in grado di ordinarlo in modo simile a ciò che potrebbe essere fatto con una query DB. Oh, ea volte una colonna/chiave deve essere specificata dal numero.

Quello che avevo in mente era qualcosa di simile a questo:

$sortOptions = array(array('city', SORT_ASC, SORT_STRING), 
         array('zip', SORT_DESC, SORT_NUMERIC), 
         array(2, SORT_ASC, SORT_STRING) // 2='last_name' 
        ); 
$sorter = new MultiSort($data, $sortOptions); 
$sortedData = $sorter->getSortedArray() ; 
print_r($jmsSorted); 

Quello che vorrei finire con questo:

Array 
(
    [0] => Array 
     (
      [first_name] => Nick 
      [last_name] => Riviera 
      [city] => Scottsdale 
      [state] => Arizona 
      [zip] => 85255 
     ) 

    [1] => Array 
     (
      [first_name] => Moe 
      [last_name] => Szyslak 
      [city] => Scottsdale 
      [state] => Arizona 
      [zip] => 85255 
     ) 

    [2] => Array 
     (
      [first_name] => Patty 
      [last_name] => Bouvier 
      [city] => Scottsdale 
      [state] => Arizona 
      [zip] => 85250 
     ) 

    [3] => Array 
     (
      [first_name] => Homer 
      [last_name] => Simpson 
      [city] => Springfield 
      [state] => Unknown 
      [zip] => 66735 
     ) 

) 

UPDATE: Penso che idealmente, un la soluzione risulterebbe nella creazione dinamica

array_multisort($city, SORT_ASC, SORT_STRING, $zip, SORT_DESC, SORT_NUMERIC, $last_name, SORT_ASC, SORT_STRING, $inputArray); 

Il il problema è che non voglio dover "hardcoded" quei nomi chiave lì dentro. Ho cercato di creare una soluzione basata su Esempio # 3 banca dati Ordinamento dei risultati dalla documentazione array_multisort() che ha finito per utilizzare array_multisort() ma io non riesco a trovare un modo per utilizzare la mia lista di argomenti costruita in modo dinamico per array_multisort().

Il mio tentativo è stato quello di "catena" tali argomenti insieme in un array e poi

call_user_func_array('array_multisort', $functionArgs); 

che si traduce in un

Warning: Parameter 2 to array_multisort() expected to be a reference, value given in... 

risposta

1

Ecco quello che ho infine optato per per essere in grado di ordinare le matrici multidimensionali. Entrambe le risposte sopra sono buone ma cercavo anche qualcosa di flessibile.

Sicuramente non penso ci sia una risposta "giusta", ma questo è ciò che funziona per i miei bisogni ed è flessibile.

Come si può vedere dalla mia @link nel commento di _usortByMultipleKeys() è stato adattato da un commento nel manuale di PHP che al momento non sembra esistere, ma credo che http://www.php.net/manual/en/function.usort.php#104398 è una nuova versione del commento originale. Non ho esplorato usando quel nuovo suggerimento.

/** 
* Sort the resultSet. 
* 
* Usage: $sortOptions = array(
*   'section', // Defaults to SORT_ASC 
*   'row' => SORT_DESC, 
*   'retail_price' => SORT_ASC); 
*  $results->sortResults($sortOptions); 
* 
* @param array $sortOptions An array of sorting instructions 
*/ 
public function sortResults(array $sortOptions) 
{ 
    usort($this->_results, $this->_usortByMultipleKeys($sortOptions)); 
} 


/** 
* Used by sortResults() 
* 
* @link http://www.php.net/manual/en/function.usort.php#103722 
*/ 
protected function _usortByMultipleKeys($key, $direction=SORT_ASC) 
{ 
    $sortFlags = array(SORT_ASC, SORT_DESC); 
    if (!in_array($direction, $sortFlags)) { 
     throw new InvalidArgumentException('Sort flag only accepts SORT_ASC or SORT_DESC'); 
    } 
    return function($a, $b) use ($key, $direction, $sortFlags) { 
     if (!is_array($key)) { //just one key and sort direction 
      if (!isset($a->$key) || !isset($b->$key)) { 
       throw new Exception('Attempting to sort on non-existent keys'); 
      } 
      if ($a->$key == $b->$key) { 
       return 0; 
      } 
      return ($direction==SORT_ASC xor $a->$key < $b->$key) ? 1 : -1; 
     } else { //using multiple keys for sort and sub-sort 
      foreach ($key as $subKey => $subAsc) { 
       //array can come as 'sort_key'=>SORT_ASC|SORT_DESC or just 'sort_key', so need to detect which 
       if (!in_array($subAsc, $sortFlags)) { 
        $subKey = $subAsc; 
        $subAsc = $direction; 
       } 
       //just like above, except 'continue' in place of return 0 
       if (!isset($a->$subKey) || !isset($b->$subKey)) { 
        throw new Exception('Attempting to sort on non-existent keys'); 
       } 
       if ($a->$subKey == $b->$subKey) { 
        continue; 
       } 
       return ($subAsc==SORT_ASC xor $a->$subKey < $b->$subKey) ? 1 : -1; 
      } 
      return 0; 
     } 
    }; 
} 
+0

Sono d'accordo sul non avere una risposta "giusta", ma maledizione, questo è molto vicino alle mie esigenze, con l'eccezione che ho bisogno di ordinare un array indicizzato, non uno associativo. –

+0

Ricevo un "Avviso: usort(): la matrice è stata modificata dalla funzione di confronto utente", utilizzando questo codice. Ho trovato la seguente spiegazione, commenterò quando posso risolverlo: http://stackoverflow.com/questions/3235387/usort-array-was-modified-by-the-user-comparison-function – vicenteherrera

1

Si potrebbe provare a utilizzare usort. Tutto quello che devi fare è creare una funzione che dica al selezionatore come ordinarla. I documenti hanno più informazioni su come farlo.

3

Questo dovrebbe funzionare per la situazione che descrivi.

usort($arrayToSort, "sortCustom"); 

function sortCustom($a, $b) 
{ 
    $cityComp = strcmp($a['city'],$b['city']); 
    if($cityComp == 0) 
    { 
     //Cities are equal. Compare zips. 
     $zipComp = strcmp($a['zip'],$b['zip']); 
     if($zipComp == 0) 
     { 
      //Zips are equal. Compare last names. 
      return strcmp($a['last_name'],$b['last_name']); 
     } 
     else 
     { 
      //Zips are not equal. Return the difference. 
      return $zipComp; 
     } 
    } 
    else 
    { 
     //Cities are not equal. Return the difference. 
     return $cityComp; 
    } 
} 

Si potrebbe condensare in una sola riga in questo modo:

function sortCustom($a, $b) 
{ 
    return ($cityComp = strcmp($a['city'],$b['city']) ? $cityComp : ($zipComp = strcmp($a['zip'],$b['zip']) ? $zipComp : strcmp($a['last_name'],$b['last_name']))); 
} 

Per quanto riguarda avere una funzione di ordinamento personalizzabile, si sta reinventando la ruota. Dai un'occhiata alla funzione array_multisort().

+0

Non sono d'accordo sul reinventare la ruota. Per quanto ne so, se voglio ordinare i dati, come una tabella di dati, dove ordino per una colonna, e ordina i dati da un'altra, 'array_multisort()' NON è la scelta giusta, poiché ordina ogni matrice in modo indipendente. Ho bisogno che vengano ordinati in un ordine specifico, in una direzione specifica, che non conosco in prima persona. La soluzione di crazyj è quella per questo caso. –

4

In PHP 5.3 ogni parametro dell'array deve essere un riferimento quando si chiama array_multisort() con call_user_func_array().

Questa funzione ordina una matrice multidimensionale e mostra un modo per creare una serie di parametri di riferimento che funzionino correttamente.

function msort() 
{ 
    $params = func_get_args(); 
    $array = array_pop($params); 

    if (!is_array($array)) 
    return false; 

    $multisort_params = array(); 
    foreach ($params as $i => $param) 
    { 
    if (is_string($param)) 
    { 
     ${"param_$i"} = array(); 
     foreach ($array as $index => $row) 
     { 
     ${"param_$i"}[$index] = $row[$param]; 
     } 
    } 
    else 
     ${"param_$i"} = $params[$i]; 

    $multisort_params[] = &${"param_$i"}; 
    } 
    $multisort_params[] = &$array; 

    call_user_func_array("array_multisort", $multisort_params); 

    return $array; 
} 

Esempio:

$ dati è l'array data dalla questione

$sorted_data = msort('city', SORT_ASC, SORT_STRING, 'zip', SORT_DESC, SORT_NUMERIC, $data) 
Problemi correlati