Introducerea: o soluție foarte generalizată pentru PHP 5.3+
Aș dori să adaug aici propria mea soluție, deoarece oferă caracteristici pe care alte răspunsuri nu le fac.
Mai exact, avantajele acestei soluții includ:
- Este reutilizabil : specificați coloana de sortare ca variabilă în loc de codul hardcod.
- Este flexibil : puteți specifica mai multe coloane de sortare (cât doriți) - coloanele suplimentare sunt utilizate ca coloane între elementele care compară inițial egal.
- Este reversibil : puteți specifica că sortarea trebuie inversată - individual pentru fiecare coloană.
- Este extensibil : dacă setul de date conține coloane care nu pot fi comparate într-un mod "prost" (de exemplu, șirul de date), puteți specifica cum să convertiți aceste elemente la o valoare care poate fi direct (de exemplu, o instanță
DateTime
).
- Asociativ dacă doriți : acest cod are grijă de sortarea elementelor, dar selectați funcția de sortare actuală (
usort
sau uasort
).
- În cele din urmă, nu se utilizează
array_multisort
: în timp ce array_multisort
este convenabil, depinde de crearea unei proiecții a tuturor datelor introduse înainte de sortare. Acest lucru consumă timp și memorie și poate fi pur și simplu prohibitiv dacă setul de date este mare.
Codul
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
};
}
Cum să utilizați
Pe parcursul acestei secțiuni voi oferi linkuri care sortează acest set de date eșantion:
$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'),
);
Cele elementare
Funcția make_comparer
acceptă un număr variabil de argumente care definesc sortarea dorită și returnează o funcție pe care ar trebui să o utilizați ca argument pentru usort
sau uasort < code>.
Cel mai simplu caz de utilizare este trecerea în cheia pe care doriți să o utilizați pentru a compara articolele de date. De exemplu, pentru a sorta $ data prin elementul name
pe care l-ați face
usort($data, make_comparer('name'));
See it in action.
Cheia poate fi, de asemenea, un număr dacă elementele sunt matrice indexate numeric. Pentru exemplul din întrebare, aceasta ar fi
usort($data, make_comparer(0));//0 = first numerically indexed column
See it in action.
Coloane de sortare multiple
You can specify Coloane de sortare multiple by passing additional parameters to make_comparer
. For example, to sort by "number" and then by the zero-indexed column:
usort($data, make_comparer('number', 0));
See it in action.
Caracteristici avansate
More Caracteristici avansate are available if you specify a sort column as an array instead of a simple string. This array should be numerically indexed, and must contain these items:
0 => the column name to sort on (mandatory)
1 => either SORT_ASC or SORT_DESC (optional)
2 => a projection function (optional)
Să vedem cum putem folosi aceste caracteristici.
Treceți invers
Pentru a sorta după nume descendent:
usort($data, make_comparer(['name', SORT_DESC]));
See it in action.
Pentru a sorta după număr descendent și apoi descendent după nume:
usort($data, make_comparer(['number', SORT_DESC], ['name', SORT_DESC]));
See it in action.
Proiecții personalizate
În anumite situații, este posibil să fie necesar să sortați după o coloană ale cărei valori nu sunt potrivite pentru sortare. Coloana "ziua de naștere" din setul de date eșantion se potrivește cu această descriere: nu are sens să comparăm zilele de naștere ca șiruri de caractere (deoarece, de exemplu, "01/01/1980" este înaintea "10/10/1970"). În acest caz, vrem să specificăm cum să proiectăm datele efective într-un formular pe care poate să fie comparat direct cu semantica dorită.
Proiecțiile pot fi specificate ca orice tip de callable : ca șiruri de caractere, matrice sau funcții anonime. O proiecție este presupusă a accepta un argument și a reveni la forma sa proiectată.
Trebuie remarcat faptul că, deși proiecțiile sunt similare cu funcțiile de comparare personalizate utilizate cu usort
și familia, ele sunt mai simple (trebuie doar să convertiți o valoare în alta) și să profitați de toate funcțiile deja coapte în make_comparer
.
Să sortăm setul de date exemplu fără o proiecție și să vedem ce se întâmplă:
usort($data, make_comparer('birthday'));
See it in action.
Asta nu a fost rezultatul dorit. Însă putem folosi date_create
ca proiecție:
usort($data, make_comparer(['birthday', SORT_ASC, 'date_create']));
See it in action.
Aceasta este ordinea corectă pe care am dorit-o.
Există multe alte lucruri pe care proiecțiile le pot realiza. De exemplu, o modalitate rapidă de a obține un tip de insensibilitate pentru majuscule este să utilizați strtolower
ca proiecție.
Acestea fiind spuse, ar trebui să menționez că este mai bine să nu folosiți proiecții dacă setul de date este mare: în acest caz ar fi mult mai rapid să proiectați toate datele manual în față și apoi să sortați fără să utilizați o proiecție, utilizarea sporită a memoriei pentru viteza de sortare mai rapidă.
În cele din urmă, iată un exemplu care folosește toate caracteristicile: mai întâi sortează numărul în ordine descrescătoare, apoi ziua de naștere ascendentă:
usort($data, make_comparer(
['number', SORT_DESC],
['birthday', SORT_ASC, 'date_create']
));
See it in action.