Abschnitt 5 · Lektion 5
Sortieren
Sortieren ist eine dieser Aufgaben, die man ständig braucht und trotzdem nie selbst schreibt. PHP bringt alles mit. Man muss nur wissen, welche der Funktionen man gerade meint, und es gibt mehr davon, als einem lieb ist.
Die Liste wird verändert, nicht zurückgegeben
<?php
$zahlen = [30, 4, 100, 7];
echo "Vorher: ", implode(", ", $zahlen), "\n";
$ergebnis = sort($zahlen);
echo "Nachher: ", implode(", ", $zahlen), "\n";
echo "Und was gibt sort zurueck?\n";
var_dump($ergebnis); Das ist der wichtigste Absatz dieser Lektion, deshalb steht er vorn. sort() gibt nicht die
sortierte Liste zurück. Es sortiert die Liste, die du hineingegeben hast, an Ort und Stelle, und
zurück kommt nur ein true dafür, dass es geklappt hat.
Das ist ungewöhnlich. array_map und array_filter aus der letzten Lektion machen es genau
andersherum: Sie lassen das Original in Ruhe und geben etwas Neues zurück. Diese beiden Sorten von
Funktionen stehen in PHP nebeneinander, und man muss von jeder wissen, zu welcher sie gehört.
Praktisch heißt das: Nach sort($zahlen); arbeitest du mit $zahlen weiter. Willst du das Original
behalten, machst du dir vorher eine Kopie, so wie im dritten Beispiel.
Der Reiter Debug zeigt beides in einem Schritt: Nach Aufzeichnen steht an der
Nachher-Zeile zahlen=[4, 7, 30, 100] und daneben ergebnis=true. Die Liste hat sich geändert,
obwohl ihr nichts zugewiesen wurde, und in der Variablen, die man für die sortierte Liste halten
könnte, liegt nur der Wahrheitswert.
Was passiert, wenn man es doch tut
<?php
$zahlen = [30, 4, 100, 7];
echo "Vor dem Sortieren\n";
// Sieht vernuenftig aus und ist falsch: in $sortiert
// landet nicht die Liste, sondern true.
$sortiert = sort($zahlen);
echo implode(", ", $sortiert), "\n";
echo "Diese Zeile wird nie erreicht\n"; Und so sieht es aus, wenn man es falsch macht. $sortiert = sort($zahlen); schreibt ein true in
die Variable, und die nächste Zeile bekommt statt einer Liste einen Wahrheitswert.
Sieh dir im Terminal beide Kanäle an. Auf dem Fehlerkanal steht ein TypeError, der ziemlich
genau sagt, was los ist: Argument 2 muss ein Array sein, gegeben wurde true. Und
php index.php; echo $? schreibt darunter die 255, den Exit-Code für einen abgestürzten Prozess
aus Lektion 1.4. Im Reiter Debug steht der Grund an der echo-Zeile als Chip: sortiert=true,
ein Wahrheitswert, wo eine Liste sein sollte.
Immerhin: Das ist ein lauter Fehler. Er bricht ab, er nennt die Zeile, und er sagt sogar dazu, was er stattdessen erwartet hätte. Wenn dir diese Meldung begegnet, ist die Ursache fast immer genau diese eine Zeile.
Die Familie
<?php
$preise = ["Hose" => 4200, "Anzug" => 12900, "Mantel" => 4800];
$a = $preise;
sort($a);
echo "sort: ";
var_dump($a);
$b = $preise;
asort($b);
echo "asort: ";
var_dump($b);
$c = $preise;
ksort($c);
echo "ksort: ";
var_dump($c); Jetzt die Übersicht. Fünf Namen, und der Unterschied steckt jeweils in ein, zwei Buchstaben:
sort()sortiert nach Wert und wirft die Schlüssel weg, nummeriert also von null an neursort()dasselbe, nur andersherumasort()sortiert nach Wert und behält die Schlüssel, dasasteht für assoziativksort()sortiert nach Schlüssel statt nach Wertusort()sortiert nach deiner eigenen Regel, dazu gleich mehr
Im Beispiel siehst du die drei wichtigsten nebeneinander, auf derselben Preisliste. Bei sort sind
die Warennamen weg und übrig bleiben nackte Zahlen mit den Nummern 0, 1, 2. Bei asort stehen die
Preise in derselben Reihenfolge, aber die Namen kleben noch dran. Und bei ksort steht der teure
Anzug plötzlich ganz oben, weil dort nicht mehr der Preis zählt, sondern der Name.
Die Frage, die alles entscheidet, ist also: Bedeutet der Schlüssel etwas? Bei einer Obstkiste
nicht, da nimmst du sort. Bei einer Preisliste sehr wohl, da nimmst du asort oder ksort.
Zwei Sätze noch zu sort() bei gemischten Werten. Wirfst du Zahlen und Text in eine Liste, kommen
bei gewöhnlichen Wörtern erst die Zahlen und dann der Text. Das ist seit PHP 8 wenigstens
vorhersehbar, vorher war es ein Ratespiel. Trotzdem: Sortier keine gemischten Listen. Es gibt
keine sinnvolle Antwort auf die Frage, ob 10 vor Apfel gehört.
Deine eigene Regel
<?php
$leute = [
["name" => "Tom", "alter" => 41],
["name" => "Mia", "alter" => 28],
["name" => "Ben", "alter" => 19],
["name" => "Ida", "alter" => 28],
];
usort($leute, fn($a, $b) => $a["alter"] <=> $b["alter"]);
foreach ($leute as $person) {
echo $person["name"], " (", $person["alter"], ")\n";
}
// Umgekehrt: die beiden Seiten tauschen.
usort($leute, fn($a, $b) => $b["alter"] <=> $a["alter"]);
echo "\nAndersherum: ", $leute[0]["name"], "\n"; Bei einer Liste von Karteikarten aus Lektion 5.3 hilft keine der vier: PHP kann nicht wissen, ob du
nach Name oder nach Alter sortieren willst. Dafür gibt es usort(), und du gibst die Regel als
Funktion mit.
Diese Funktion bekommt zwei Einträge und beantwortet die Frage: Wer kommt zuerst? Sie gibt eine
negative Zahl zurück, wenn der erste vorn steht, eine positive, wenn der zweite vorn steht, und
0, wenn es egal ist.
Von Hand schreibt man das nicht mehr, denn dafür gibt es den Raumschiff-Operator <=>. Er nimmt
zwei Werte und liefert genau diese drei Antworten. $a["alter"] <=> $b["alter"] heißt also
schlicht: nach Alter aufsteigend. Willst du absteigend, tauschst du die beiden Seiten, so wie am
Ende des Beispiels.
Und eine Zusage, auf die du dich verlassen kannst: Bei Gleichstand bleibt die Reihenfolge aus der Ursprungsliste. Mia und Ida sind beide 28, und Mia stand vorher weiter oben, also steht sie es danach auch. Seit PHP 8 gilt das für alle Sortierfunktionen. Vorher war es Glückssache, und Übersichtsseiten sprangen bei jedem Neuladen leicht durcheinander.
Zum Mitnehmen
PHP hat für das Sortieren nicht eine Funktion, sondern eine ganze Familie. Auseinander hält man sie mit einer einzigen Frage: Was passiert mit den Schlüsseln? Und mit einer zweiten, die den häufigsten Fehler damit erklärt: Was kommt eigentlich zurück?
Jetzt du
Basis Konto, kostenlosZu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.
Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.
In diesem Kurs läuft dein Code auf einem Server. Dafür hat das Basis Konto 1 Stunde im Monat, mehr Zeit gibt es mit dem Premium Konto.
Was in dieser Lektion steckt
-
Artikel mit 4 Beispielen zum Ausprobieren
Steht hier, ohne Konto lesbar.
-
Aufgabe, dein Code läuft auf einem Server
Öffnet sich mit dem Basis Konto.