Présentation : une solution très généralisée pour PHP 5.3+.
J'aimerais ajouter ma propre solution ici, car elle offre des fonctionnalités que les autres réponses n'offrent pas.
Plus précisément, les avantages de cette solution sont les suivants :
- C'est réutilisable : vous spécifiez la colonne de tri comme une variable au lieu de la coder en dur.
- C'est flexible Vous pouvez spécifier plusieurs colonnes de tri (autant que vous le souhaitez). Les colonnes supplémentaires sont utilisées pour départager les éléments qui se comparent initialement.
- C'est réversible vous pouvez spécifier que le tri doit être inversé -- individuellement pour chaque colonne.
- C'est extensible si l'ensemble de données contient des colonnes qui ne peuvent pas être comparées de manière "muette" (par exemple, des chaînes de dates), vous pouvez également spécifier comment convertir ces éléments en une valeur qui peut être directement comparée (par exemple, une chaîne de dates).
DateTime
instance).
- C'est associatif si vous voulez ce code s'occupe de trier les éléments, mais vous sélectionner la fonction de tri réelle (
usort
o uasort
).
- Enfin, il n'utilise pas
array_multisort
: tandis que array_multisort
est pratique, elle dépend de la création d'une projection de toutes vos données d'entrée avant le tri. Cela consomme du temps et de la mémoire et peut être tout simplement prohibitif si votre ensemble de données est important.
Le code
function make_comparer() {
// Normalize criteria up front so that the comparer finds everything tidy
$criteria = func_get_args();
foreach ($criteria as $index => $criterion) {
$criteria[$index] = is_array($criterion)
? array_pad($criterion, 3, null)
: array($criterion, SORT_ASC, null);
}
return function($first, $second) use (&$criteria) {
foreach ($criteria as $criterion) {
// How will we compare this round?
list($column, $sortOrder, $projection) = $criterion;
$sortOrder = $sortOrder === SORT_DESC ? -1 : 1;
// If a projection was defined project the values now
if ($projection) {
$lhs = call_user_func($projection, $first[$column]);
$rhs = call_user_func($projection, $second[$column]);
}
else {
$lhs = $first[$column];
$rhs = $second[$column];
}
// Do the actual comparison; do not return if equal
if ($lhs < $rhs) {
return -1 * $sortOrder;
}
else if ($lhs > $rhs) {
return 1 * $sortOrder;
}
}
return 0; // tiebreakers exhausted, so $first == $second
};
}
Mode d'emploi
Tout au long de cette section, je fournirai des liens permettant de trier cet ensemble de données types :
$data = array(
array('zz', 'name' => 'Jack', 'number' => 22, 'birthday' => '12/03/1980'),
array('xx', 'name' => 'Adam', 'number' => 16, 'birthday' => '01/12/1979'),
array('aa', 'name' => 'Paul', 'number' => 16, 'birthday' => '03/11/1987'),
array('cc', 'name' => 'Helen', 'number' => 44, 'birthday' => '24/06/1967'),
);
L'essentiel
La fonction make_comparer
accepte un nombre variable d'arguments qui définissent le tri souhaité et renvoie une fonction que vous êtes censé utiliser comme argument de la fonction usort
o uasort
.
Le cas d'utilisation le plus simple consiste à transmettre la clé que vous souhaitez utiliser pour comparer des éléments de données. Par exemple, pour trier $data
par le name
ce que vous feriez
usort($data, make_comparer('name'));
Voyez-le en action .
La clé peut également être un nombre si les éléments sont des tableaux à indexation numérique. Dans l'exemple de la question, il s'agirait de
usort($data, make_comparer(0)); // 0 = first numerically indexed column
Voyez-le en action .
Colonnes de tri multiples
Vous pouvez spécifier plusieurs colonnes de tri en passant des paramètres supplémentaires à la commande make_comparer
. Par exemple, pour trier par "nombre" et ensuite par la colonne indexée zéro :
usort($data, make_comparer('number', 0));
Voyez-le en action .
Fonctions avancées
Des fonctionnalités plus avancées sont disponibles si vous spécifiez une colonne de tri sous forme de tableau au lieu d'une simple chaîne. Ce tableau doit être indexé numériquement, et doit contenir ces éléments :
0 => the column name to sort on (mandatory)
1 => either SORT_ASC or SORT_DESC (optional)
2 => a projection function (optional)
Voyons comment nous pouvons utiliser ces fonctionnalités.
Triage inverse
Pour trier par nom en ordre décroissant :
usort($data, make_comparer(['name', SORT_DESC]));
Voir en action .
Pour trier par numéro en ordre décroissant, puis par nom en ordre décroissant :
usort($data, make_comparer(['number', SORT_DESC], ['name', SORT_DESC]));
Voyez-le en action .
Projections personnalisées
Dans certains scénarios, vous pouvez avoir besoin de trier par une colonne dont les valeurs ne se prêtent pas bien au tri. La colonne "anniversaire" de l'ensemble de données d'exemple correspond à cette description : il n'est pas logique de comparer les anniversaires sous forme de chaînes de caractères (parce que, par exemple, "01/01/1980" vient avant "10/10/1970"). Dans ce cas, nous voulons spécifier comment projet les données réelles dans un formulaire qui puede être comparé directement avec la sémantique souhaitée.
Les projections peuvent être spécifiées comme n'importe quel type de Appelable comme des chaînes de caractères, des tableaux, ou des fonctions anonymes. Une projection est supposée accepter un argument et retourner sa forme projetée.
Il convient de noter que, bien que les projections soient similaires aux fonctions de comparaison personnalisées utilisées avec le logiciel usort
et de la famille, elles sont plus simples (il suffit de convertir une valeur en une autre) et tirent parti de toutes les fonctionnalités déjà intégrées dans le module make_comparer
.
Trions l'ensemble de données de l'exemple sans projection et voyons ce qui se passe :
usort($data, make_comparer('birthday'));
Voyez-le en action .
Ce n'était pas le résultat souhaité. Mais nous pouvons utiliser date_create
comme une projection :
usort($data, make_comparer(['birthday', SORT_ASC, 'date_create']));
Voyez-le en action .
C'est l'ordre correct que nous voulions.
Il y a beaucoup plus de choses que les projections peuvent réaliser. Par exemple, un moyen rapide d'obtenir un tri insensible à la casse est d'utiliser strtolower
comme une projection.
Cela dit, je dois également mentionner qu'il est préférable de ne pas utiliser de projections si votre ensemble de données est volumineux : dans ce cas, il serait beaucoup plus rapide de projeter toutes vos données manuellement dès le départ, puis de trier sans utiliser de projection, bien que cela implique une utilisation accrue de la mémoire pour une vitesse de tri plus rapide.
Enfin, voici un exemple qui utilise toutes les fonctionnalités : il trie d'abord par numéro en ordre décroissant, puis par date d'anniversaire en ordre croissant :
usort($data, make_comparer(
['number', SORT_DESC],
['birthday', SORT_ASC, 'date_create']
));
Voyez-le en action .
7 votes
(2 ans plus tard...) Si vous triez des dates stockées sous forme de chaînes de caractères, vous devrez peut-être utiliser strtotime [1]. docs.php.net/manual/fen/function.strtotime.php
0 votes
@deceze, stackoverflow.com/q/1597736/1709587 semble être une meilleure cible de dupe pour moi. C'est une copie plus exacte et les réponses vont donc plus vite au but que les vôtres. stackoverflow.com/a/17364128/1709587 tout en ayant collectivement le même niveau de détail. Que dites-vous de changer de cible ? (Divulgation : je suis peut-être partial en tant qu'auteur d'une des réponses à la cible dupe que je propose).
0 votes
Voir aussi : stackoverflow.com/questions/1597736/