mitmario.dev

Arrays umformen

PHP Sandbox 3 Min Lesezeit 4 BeispieleLektion 4 von 7

Bis jetzt hast du jede Umformung selbst geschleift: neue Variable anlegen, foreach darüber, Ergebnis anhängen. Das ist völlig richtig und wird immer funktionieren. Für die drei häufigsten Fälle gibt es aber je eine fertige Funktion, und der Code wird damit kürzer und vor allem eindeutiger, weil schon der Name sagt, was passiert.

Jeden Wert durch dieselbe Mühle

Aus jedem Wert einen neuen machen
<?php

$cent = ["Brot" => 250, "Milch" => 120, "Kaffee" => 490];

// Erst das Ding, das gemacht werden soll, dann die Liste:
$euro = array_map(fn($betrag) => $betrag / 100, $cent);

var_dump($euro);

echo "\nZum Vergleich, von Hand geschrieben:\n";

$selbst = [];
foreach ($cent as $ware => $betrag) {
    $selbst[$ware] = $betrag / 100;
}
var_dump($selbst);

array_map() nimmt eine Liste und gibt eine neue Liste zurück, die genauso lang ist. Jeder Wert ist vorher durch deine Funktion gelaufen. Die ursprüngliche Liste bleibt dabei unangetastet, und das ist ein Unterschied zu den Sortierfunktionen in der nächsten Lektion.

Der zweite Teil des Beispiels zeigt dieselbe Arbeit von Hand. Vier Zeilen statt einer, und die Fehler stecken alle in den vier: die neue Variable vergessen, den Schlüssel verlieren, aus Versehen in die alte Liste schreiben.

Das fn($betrag) => $betrag / 100 ist eine anonyme Funktion, eine Funktion ohne Namen. Links steht, was hineingeht, rechts, was herauskommt. Mehr musst du hier nicht darüber wissen, ausführlich kommt das in Lektion 6.6.

Eine Kleinigkeit, die später wichtig wird: Mit einer einzigen Liste behält array_map die Schlüssel. Aus Brot wird wieder Brot. Gibst du zwei Listen mit, nummeriert es das Ergebnis neu durch, weil es dann ja nicht wissen kann, wessen Schlüssel gelten sollen.

Nur behalten, was passt

Nur behalten, was passt
<?php

$zahlen = [10, 20, 30, 40, 50];

// Hier andersherum: erst die Liste, dann die Bedingung.
$gross = array_filter($zahlen, fn($wert) => $wert > 25);

echo "Gefiltert:\n";
var_dump($gross);

echo "\nDer erste Eintrag steht auf Platz 0?\n";
var_dump($gross[0] ?? "gibt es nicht");

echo "\nMit array_values neu nummeriert:\n";
var_dump(array_values($gross));

array_filter() geht die Liste durch und behält jeden Eintrag, für den deine Bedingung true ergibt. Alles andere fällt raus.

Und jetzt die Sache, die einen beim ersten Mal wirklich erwischt: Die Schlüssel bleiben, wie sie waren. Fällt der Eintrag auf Platz 0 heraus, fängt das Ergebnis bei Platz 2 an. Kein Fehler, keine Warnung, es ist einfach so. Wer danach mit $gefiltert[0] zugreift, bekommt genau die Warnung aus Lektion 5.1 und null obendrauf.

Bei Textschlüsseln ist das genau richtig, dort will man den Namen ja behalten. Bei nummerierten Listen ist es meistens im Weg, und dann hilft array_values(): Es wirft die Schlüssel weg und nummeriert von null an neu durch.

Faustregel: Nach einem array_filter auf einer nummerierten Liste kommt array_values.

Zusammenzählen

Sieben, rechnen, summieren
<?php

$preise = ["Hemd" => 3900, "Socken" => 490, "Hose" => 4200];

$teuer = array_filter($preise, fn($cent) => $cent >= 3000);
$rabatt = array_map(fn($cent) => $cent * 0.9, $teuer);

foreach ($rabatt as $ware => $cent) {
    echo $ware, ": ", number_format($cent / 100, 2, ",", "."), "\n";
}

echo "Summe: ", number_format(array_sum($rabatt) / 100, 2, ",", "."), "\n";
echo "Artikel: ", count($rabatt), "\n";

array_sum() addiert alle Werte einer Liste. Das ist die dritte im Bunde und die langweiligste, aber sie steht selten allein.

Das Beispiel zeigt die übliche Reihenfolge: erst sieben, dann rechnen, dann summieren. Jede Zeile macht genau eine Sache, und du kannst nach jeder ein var_dump einschieben, um zu sehen, wo es schiefgeht. Genau so würde ich das auch nach zwanzig Jahren noch bauen.

Beachte, dass hier weiter in Cent gerechnet wird und erst beim Ausgeben durch 100 geteilt wird, so wie es Lektion 2.3 empfiehlt. Die Formatierung mit number_format kennst du aus Lektion 3.4.

Die verdrehte Reihenfolge

Die Reihenfolge der Argumente
<?php

$zahlen = [1, 2, 3];

// array_map: erst die Funktion, dann die Liste.
var_dump(array_map(fn($z) => $z * 10, $zahlen));

// array_filter: erst die Liste, dann die Funktion.
var_dump(array_filter($zahlen, fn($z) => $z > 1));

// Und ohne Funktion siebt array_filter alles Falsche aus,
// nach der Liste aus Lektion 2.5:
var_dump(array_filter([0, 1, "", "a", null, "0", []]));

Zum Schluss die Kleinigkeit, die einen jedes Mal ärgert: array_map will erst die Funktion, array_filter will erst die Liste. Das ist historisch gewachsen und lässt sich nicht mehr reparieren, ohne millionenfach vorhandenen Code zu brechen.

Merk dir das nicht. Schlag es nach, wenn du es brauchst, oder lass es dir vom Editor vorschlagen. Wer sich daran festbeißt, verliert Zeit an etwas, das nichts erklärt.

Was du dagegen mitnehmen kannst: array_filter ohne Funktion siebt alles aus, was PHP für falsch hält. Das ist genau die Liste aus Lektion 2.5, und deshalb überlebt der Text "0" diese Behandlung nicht. Praktisch zum Aufräumen einer Liste mit Lücken, gefährlich, wenn eine echte Null darin ein gültiger Wert ist.

Zum Mitnehmen

Aus einer Liste eine andere machen, ohne selbst zu schleifen. Drei Funktionen reichen dafür in den allermeisten Fällen, und sie ersparen dir eine Menge Zeilen, in denen man sich verzählen kann.

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.