Matrice di ordinamento di matrici multidimensionali su più di una "colonna" (chiave) con opzioni di ordinamento specificate

StackOverflow https://stackoverflow.com/questions/809771

Domanda

Sto cercando di essere in grado di ordinare una matrice di array multidimensionali su più di una colonna. Per complicarlo ulteriormente, vorrei poter impostare opzioni di ordinamento specifiche per chiave / colonna. Ho quello che è simile al risultato di una query DB, ma in realtà non proviene da uno, quindi la necessità di ordinarlo in PHP anziché in SQL.

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 poterlo ordinare in modo simile a quello che si potrebbe fare con una query DB. Oh, e talvolta una colonna / chiave deve essere specificata per 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 è 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
        )

)

AGGIORNAMENTO: Penso che idealmente una soluzione si tradurrebbe in una creazione dinamica

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

Il problema è che non voglio dover "codificare" " quei nomi chiave lì dentro. Ho provato a creare una soluzione basata su Esempio n. 3 Ordinamento dei risultati del database dalla array_multisort () che ha finito per usare array_multisort () ma non riesco a trovare un modo per usare il mio elenco di argomenti costruito dinamicamente per array_multisort () .

Il mio tentativo è stato di "quotare" " quegli argomenti insieme in un array e poi

call_user_func_array( 'array_multisort', $functionArgs);

Ciò si traduce in un

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

Soluzione 4

Ecco cosa mi sono finalmente deciso per poter ordinare gli array multidimensionali. Entrambe le risposte sopra sono buone ma stavo anche cercando qualcosa di flessibile.

Non credo assolutamente che ci sia una risposta "giusta", ma questo è ciò che funziona per le mie esigenze ed è flessibile.

Come puoi vedere dal mio @link nel commento di _usortByMultipleKeys () è stato adattato da un commento nel manuale di PHP che attualmente non sembra esistere , ma credo http://www.php.net/manual/en /function.usort.php#104398 è una nuova versione del commento originale. Non ho esplorato usando questo 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;
        }
    };
}

Altri suggerimenti

In PHP 5.3 ogni parametro nella matrice deve essere un riferimento quando si chiama array_multisort () con call_user_func_array () .

Questa funzione ordina un array multidimensionale e mostra un modo per costruire un array di parametri referenziati che funziona 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:

$ data è l'array dato dalla domanda

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

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;
    }
}

Potresti condensarlo in una 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 una funzione di ordinamento personalizzabile, stai reinventando la ruota. Dai un'occhiata al array_multisort () funzione.

Potresti provare a utilizzare usort . Tutto quello che devi fare è creare una funzione che indichi allo smistatore come ordinarlo. I documenti hanno maggiori informazioni su come farlo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top