mitmario.dev

Sortieren

PHP Sandbox 4 Min Lesezeit 4 BeispieleLektion 5 von 7

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

Die Liste ändert sich selbst
<?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

Der teuerste Fehler damit
<?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

Wer die Schlüssel behält
<?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 neu
  • rsort() dasselbe, nur andersherum
  • asort() sortiert nach Wert und behält die Schlüssel, das a steht für assoziativ
  • ksort() sortiert nach Schlüssel statt nach Wert
  • usort() 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

Eigene Regeln mit usort
<?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, kostenlos

Zu 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.