Wie sortiere ich ein mehrdimensionales Array in PHP [duplicate]
Lesezeit: 13 Minuten
Melikoth
Ich habe CSV-Daten in ein mehrdimensionales Array geladen. Auf diese Weise ist jede “Zeile” ein Datensatz und jede “Spalte” enthält denselben Datentyp. Ich verwende die folgende Funktion, um meine CSV-Datei zu laden.
Ich muss in der Lage sein, eine zu sortierende Spalte anzugeben, damit die Zeilen neu angeordnet werden. Eine der Spalten enthält Datumsinformationen im Format von Y-m-d H:i:s und ich möchte in der Lage sein, mit dem neuesten Datum in der ersten Zeile zu sortieren.
(2 Jahre später …) Wenn Sie Daten sortieren, die als Zeichenfolgen gespeichert sind, müssen Sie möglicherweise zuerst strtotime verwenden [1] docs.php.net/manual/en/function.strtotime.php
– Dan Burton
19. Mai 2010 um 18:33 Uhr
@deceze, stackoverflow.com/q/1597736/1709587 scheint mir ein besseres Dupe-Ziel zu sein. Es ist ein genaueres Duplikat und die Antworten dort kommen folglich schneller auf den Punkt als Ihre unter stackoverflow.com/a/17364128/1709587, während sie insgesamt den gleichen Detailgrad haben. Was sagt ihr zum Zielwechsel? (Offenlegung: Ich bin möglicherweise als Autor einer der Antworten auf das von mir vorgeschlagene betrügerische Ziel voreingenommen.)
– Mark Amery
29. Juni 2019 um 16:34 Uhr
Siehe auch: stackoverflow.com/questions/1597736/…
– dreftymac
29. Juli 2019 um 21:30 Uhr
Jon
Wir stellen vor: eine sehr verallgemeinerte Lösung für PHP 5.3+
Ich möchte hier meine eigene Lösung hinzufügen, da sie Funktionen bietet, die andere Antworten nicht bieten.
Zu den Vorteilen dieser Lösung gehören insbesondere:
Es ist wiederverwendbar: Sie geben die Sortierspalte als Variable an, anstatt sie fest zu codieren.
Es ist flexibel: Sie können mehrere Sortierspalten angeben (so viele Sie möchten) – zusätzliche Spalten werden als Tiebreaker zwischen Elementen verwendet, die anfänglich gleich verglichen werden.
Es ist reversibel: Sie können angeben, dass die Sortierung umgekehrt werden soll – individuell für jede Spalte.
Es ist erweiterbar: Wenn der Datensatz Spalten enthält, die nicht auf “dumme” Weise verglichen werden können (z. B. Datumszeichenfolgen), können Sie auch angeben, wie diese Elemente in einen direkt vergleichbaren Wert konvertiert werden sollen (z. B. a DateTime Beispiel).
Es ist assoziativ, wenn Sie wollen: Dieser Code kümmert sich um das Sortieren von Artikeln, aber Sie Wählen Sie die aktuelle Sortierfunktion (usort oder uasort).
Schließlich nutzt es nicht array_multisort: während array_multisort bequem ist, hängt es davon ab, vor dem Sortieren eine Projektion aller Ihrer Eingabedaten zu erstellen. Dies verbraucht Zeit und Speicherplatz und kann einfach unerschwinglich sein, wenn Ihr Datensatz groß ist.
Der 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
};
}
Wie benutzt man
In diesem Abschnitt werde ich Links bereitstellen, die diesen Beispieldatensatz sortieren:
Die Funktion make_comparer akzeptiert eine variable Anzahl von Argumenten, die die gewünschte Sortierung definieren, und gibt eine Funktion zurück, die Sie als Argument verwenden sollen usort oder uasort.
Der einfachste Anwendungsfall besteht darin, den Schlüssel zu übergeben, den Sie zum Vergleichen von Datenelementen verwenden möchten. Zum Beispiel sortieren $data bis zum name Artikel, den Sie tun würden
Sie können mehrere Sortierspalten angeben, indem Sie zusätzliche Parameter an übergeben make_comparer. So sortieren Sie beispielsweise nach “Nummer” und dann nach der mit Null indizierten Spalte:
Erweiterte Funktionen sind verfügbar, wenn Sie eine Sortierspalte als Array anstelle einer einfachen Zeichenfolge angeben. Dieses Array sollte numerisch indiziert sein und diese Elemente enthalten:
0 => the column name to sort on (mandatory)
1 => either SORT_ASC or SORT_DESC (optional)
2 => a projection function (optional)
Mal sehen, wie wir diese Funktionen nutzen können.
In einigen Szenarien müssen Sie möglicherweise nach einer Spalte sortieren, deren Werte sich nicht gut zum Sortieren eignen. Die Spalte „Geburtstag“ im Beispieldatensatz passt zu dieser Beschreibung: Es macht keinen Sinn, Geburtstage als Zeichenfolgen zu vergleichen (weil zB „01.01.1980“ vor „10.10.1970“ steht). In diesem Fall möchten wir angeben, wie Projekt die eigentlichen Daten in ein Formular, das kann direkt mit der gewünschten Semantik verglichen werden.
Projektionen können als beliebiger Typ angegeben werden abrufbar: als Strings, Arrays oder anonyme Funktionen. Es wird angenommen, dass eine Projektion ein Argument akzeptiert und seine projizierte Form zurückgibt.
Es sollte beachtet werden, dass zwar Projektionen den benutzerdefinierten Vergleichsfunktionen ähneln, die mit verwendet werden usort und Familie, sie sind einfacher (Sie müssen nur einen Wert in einen anderen umwandeln) und nutzen alle bereits eingebauten Funktionen make_comparer.
Lassen Sie uns den Beispieldatensatz ohne Projektion sortieren und sehen, was passiert:
Dies ist die richtige Reihenfolge, die wir wollten.
Es gibt noch viel mehr Dinge, die Projektionen erreichen können. Eine schnelle Möglichkeit, eine Sortierung ohne Berücksichtigung der Groß-/Kleinschreibung zu erhalten, ist z. B. use strtolower als Projektion.
Allerdings sollte ich auch erwähnen, dass es besser ist, keine Projektionen zu verwenden, wenn Ihr Datensatz groß ist: In diesem Fall wäre es viel schneller, alle Ihre Daten manuell im Voraus zu projizieren und dann ohne Verwendung einer Projektion zu sortieren, obwohl dies der Fall wäre erhöhte Speichernutzung für schnellere Sortiergeschwindigkeit.
Zum Schluss noch ein Beispiel, das alle Features nutzt: Es sortiert erst nach Nummer absteigend, dann nach Geburtstag aufsteigend:
@Jon – Danke für all deine Hilfe. Ich habe versucht, dies auf meinem Server zu implementieren, und ich bekomme Folgendes: PHP-Parse-Fehler: Syntaxfehler, unerwartet ‘[‘, expecting ‘)’ — I tried different variation, I’m just a bit lost on how this really works. I’m on v5.3.28 – ideone.com runs php 5.4 – is that the issue?
– Ecropolis
Jun 5, 2014 at 20:34
@Ecropolis: PHP 5.3 does not support the short [...] Array-Syntax, müssen Sie verwenden array(...) stattdessen. Ich habe das in den Beispielen der Kürze halber nicht gemacht, aber make_comparer selbst ist kompatibel mit 5.3.
– Jon
5. Juni 2014 um 20:49 Uhr
@ Jon Tolle Antwort und ich stimme zu, dass dies eher eine Website als eine Antwort ist. Danke. Habe nur eine Frage. Wie kann ich es zum Laufen bringen, um nach Objekten zu rennen?
– YahyaE
3. Oktober 2014 um 21:21 Uhr
@YahyaE: Arrays von Objekten sortieren? Ersetzen $first[$column] mit $first->$columnund dasselbe für $second. Insgesamt vier Auswechslungen.
– Jon
10. Oktober 2014 um 0:02 Uhr
@CalculatingMachine Ich habe die Frage gelesen, aber Sie zeigen nicht, was Sie versucht haben. Es scheint gerecht usort($data['content'], get_comparer('price')) würde reichen, bin mir aber nicht sicher.
In diesem Beispiel könnte $mdarray also ein zweidimensionales Array sein, etwa ein Array von Datenbankeinträgen. In diesem Beispiel ist 0 der Index der Spalte „Datum“ in jedem Datensatz (oder jeder Zeile). Sie konstruieren also das Array $dates (im Grunde das gleiche Array, aber nur mit dieser Spalte) und weisen die Funktion array_multisort an, $mdarray basierend auf den Werten dieser bestimmten Spalte zu sortieren.
– Dan Burton
19. Mai 2010 um 18:26 Uhr
Zur Verdeutlichung können Sie am Anfang dieses Beispiels etwas hinzufügen $dates = array();
– Dan Burton
19. Mai 2010 um 18:27 Uhr
Soll array_multisort mit assoziativen Arrays funktionieren (ändern $row[0] zu $row['whatever']? Gehen Sie hier nicht. Nachdem ich mein Array in numerisch geändert hatte, funktionierte die Funktion wie erwartet.
– ein Codierer
14. Februar 2013 um 22:24 Uhr
Ist nicht die Einbeziehung der $key unnötig bei der Nutzung array_multisort()? Es scheint einfacher und beabsichtigter zu schreiben foreach ($mdarray as $row) { $sortByDate[] = $row['date']; } dann array_multisort( $sortByDate, SORT_DESC, $mdarray ); (Ihre semantische Laufleistung kann variieren).
– Markus Fuchs
13. März 2013 um 21:02 Uhr
Wenn array_multi_sort() ist die Antwort, die Frage wurde nicht verstanden. Obwohl es technisch funktionieren wird, gibt es normalerweise eine bessere Lösung mit einer benutzergenerierten Vergleichsfunktion und der Verwendung von a usort() Funktion. Es ist einfacher zu pflegen. Bei Multisort erstellen Sie normalerweise Code, der die Daten für die Sortierung vorbereitet. Wenn sich die Datenstruktur ändert, wird dieser Code möglicherweise verworfen. Mit usort() ändern Sie die Vergleichsfunktion – genauso wie Sie die Datenstruktur geändert haben.
– Sven
17. Juli 2013 um 7:54 Uhr
Mit usort. Hier ist eine generische Lösung, die Sie für verschiedene Spalten verwenden können:
class TableSorter {
protected $column;
function __construct($column) {
$this->column = $column;
}
function sort($table) {
usort($table, array($this, 'compare'));
return $table;
}
function compare($a, $b) {
if ($a[$this->column] == $b[$this->column]) {
return 0;
}
return ($a[$this->column] < $b[$this->column]) ? -1 : 1;
}
}
So sortieren Sie nach der ersten Spalte:
$sorter = new TableSorter(0); // sort by first column
$mdarray = $sorter->sort($mdarray);
Ich erhalte einen Parse-Fehler: Parse-Fehler, unerwarteter T_STRING, erwartet T_OLD_FUNCTION oder T_FUNCTION oder T_VAR oder ‘}’ in der zweiten Zeile dieser Klasse.
– Melikot
18. September 2008 um 21:35 Uhr
Ersetzen Sie “protected” durch “var” und “__construct” durch “TableSorter”, und es funktioniert in PHP4. Beachten Sie jedoch, dass PHP4 eingestellt wurde.
– troelskn
18. September 2008 um 22:23 Uhr
Ich habe PHP auf v5 gesetzt, wusste nicht, dass es standardmäßig v4 ausführt. Nachdem ich es mir eine Weile angeschaut habe, glaube ich zu verstehen, wie man es auch für verschiedene Arten von Sorten modifiziert
– Melikot
18. September 2008 um 23:19 Uhr
Mehrreihiges Sortieren mit Closure
Hier ist ein weiterer Ansatz mit uasort() und einer anonymen Callback-Funktion (Closure). Ich habe diese Funktion regelmäßig genutzt. PHP 5.3 erforderlich – keine Abhängigkeiten mehr!
Ich weiß, es ist 2 Jahre her, seit diese Frage gestellt und beantwortet wurde, aber hier ist eine andere Funktion, die ein zweidimensionales Array sortiert. Es akzeptiert eine variable Anzahl von Argumenten, sodass Sie mehr als einen Schlüssel (z. B. Spaltennamen) zum Sortieren übergeben können. PHP 5.3 erforderlich.
function sort_multi_array ($array, $key)
{
$keys = array();
for ($i=1;$i<func_num_args();$i++) {
$keys[$i-1] = func_get_arg($i);
}
// create a custom search function to pass to usort
$func = function ($a, $b) use ($keys) {
for ($i=0;$i<count($keys);$i++) {
if ($a[$keys[$i]] != $b[$keys[$i]]) {
return ($a[$keys[$i]] < $b[$keys[$i]]) ? -1 : 1;
}
}
return 0;
};
usort($array, $func);
return $array;
}
(2 Jahre später …) Wenn Sie Daten sortieren, die als Zeichenfolgen gespeichert sind, müssen Sie möglicherweise zuerst strtotime verwenden [1] docs.php.net/manual/en/function.strtotime.php
– Dan Burton
19. Mai 2010 um 18:33 Uhr
@deceze, stackoverflow.com/q/1597736/1709587 scheint mir ein besseres Dupe-Ziel zu sein. Es ist ein genaueres Duplikat und die Antworten dort kommen folglich schneller auf den Punkt als Ihre unter stackoverflow.com/a/17364128/1709587, während sie insgesamt den gleichen Detailgrad haben. Was sagt ihr zum Zielwechsel? (Offenlegung: Ich bin möglicherweise als Autor einer der Antworten auf das von mir vorgeschlagene betrügerische Ziel voreingenommen.)
– Mark Amery
29. Juni 2019 um 16:34 Uhr
Siehe auch: stackoverflow.com/questions/1597736/…
– dreftymac
29. Juli 2019 um 21:30 Uhr