Tableau de tri de tableaux multi-dimensionnels sur plus d'une "colonne" (clé) avec les options de tri spécifiées

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

Question

Je souhaite pouvoir trier un tableau de tableaux multidimensionnels sur plusieurs colonnes. Pour compliquer encore les choses, j'aimerais pouvoir définir des options de tri spécifiques par clé / colonne. J'ai ce qui est similaire au résultat d'une requête en base de données, mais je ne viens pas d'une telle requête, d'où la nécessité de la trier en PHP plutôt qu'en 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
        )

)

J'aimerais pouvoir le trier de la même manière que ce qui pourrait être fait avec une requête de base de données. Oh, et parfois une colonne / clé doit être spécifiée par un numéro.

Ce à quoi je pensais ressemblait à ceci:

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

Ce que je voudrais finir est la suivante:

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: je pense que, dans l'idéal, une solution résulterait en une création dynamique

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

Le problème est que je ne veux pas avoir à "coder en dur" ces noms de clés sont là. J'ai essayé de créer une solution basée sur Exemple # 3 Tri des résultats de la base de données à partir de array_multisort () documentation qui a fini par utiliser array_multisort () mais je n'arrive pas à trouver un moyen d'utiliser ma liste d'arguments construite dynamiquement pour array_multisort () .

Ma tentative était de "chaîner" ces arguments ensemble dans un tableau, puis

call_user_func_array( 'array_multisort', $functionArgs);

Cela se traduit par un

Warning: Parameter 2 to array_multisort() expected to be a reference, value given in...
Était-ce utile?

La solution 4

Voici ce sur quoi j’ai finalement décidé de pouvoir trier des tableaux multidimensionnels. Les deux réponses ci-dessus sont bonnes, mais je cherchais aussi quelque chose de flexible.

Je ne pense vraiment pas qu’il existe une réponse «juste», mais c’est ce qui fonctionne pour mes besoins et qui est flexible.

Comme vous pouvez le voir sur mon @link dans le commentaire de _usortByMultipleKeys () , il a été adapté d'un commentaire du manuel PHP qui semble ne pas exister actuellement. , mais je crois http://www.php.net/manual/fr /function.usort.php#104398 est une nouvelle version du commentaire original. Je n'ai pas exploré l'utilisation de cette nouvelle suggestion.

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

Autres conseils

En PHP 5.3, chaque paramètre du tableau doit être une référence lors de l'appel de array_multisort () avec call_user_func_array () .

Cette fonction trie un tableau multidimensionnel et montre un moyen de construire un tableau de paramètres référencés qui fonctionne correctement.

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

Exemple:

$ data est le tableau donné à partir de la question

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

Cela devrait fonctionner dans la situation que vous décrivez.

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

Vous pouvez le condenser en une seule ligne, comme suit:

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

En ce qui concerne la fonction de tri personnalisable, vous réinventez la roue. Consultez le array_multisort () fonction.

Vous pouvez essayer d'utiliser usort . Tout ce que vous avez à faire est de créer une fonction qui indique à la trieuse comment la trier. Les docs ont plus d’informations sur la façon de le faire.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top