mitmario.dev

Synthese: Eingaben aufräumen

JavaScript Im Browser 4 Min Lesezeit 4 BeispieleLektion 5 von 6

Diese Lektion bringt Abschnitt 8 zusammen und schlägt die Brücke zu Abschnitt 13. Dort kommt der Text aus einem echten Formularfeld, und dann ist genau das hier die Arbeit, die davor passieren muss.

Erst zerlegen, dann säubern, dann prüfen

Die Reihenfolge der Handgriffe
const eingabe = "\n  Ida;98\nBen ; 91\n\nMila;87\n";

// 1. Zerlegen, weil die Struktur an den Zeilen hängt.
const roh = eingabe.split("\n");
console.log("1. zerlegt:", roh);

// 2. Trimmen, jede Zeile einzeln.
const getrimmt = roh.map((zeile) => zeile.trim());
console.log("2. getrimmt:", getrimmt);

// 3. Leeres wegwerfen. Erst jetzt, denn vorher waren die
//    Zeilen mit Leerzeichen noch nicht leer.
const zeilen = getrimmt.filter((zeile) => zeile !== "");
console.log("3. gefiltert:", zeilen);

// 4. Umformen, wenn die Form steht.
const eintraege = zeilen.map((zeile) => {
  const [name, punkte] = zeile.split(";");
  return { name: name.trim(), punkte: Number(punkte.trim()) };
});
console.log("4. umgeformt:", eintraege);

Die Handgriffe kennst du alle aus den vorherigen Lektionen. Neu ist nur, in welcher Reihenfolge sie kommen, und die ist nicht beliebig.

Zerlegen kommt zuerst, weil die Struktur an den Trennzeichen hängt und die noch da sein müssen. Trimmen kommt danach, denn die Leerzeichen sitzen an den Rändern der Teile, nicht an denen des ganzen Textes.

Filtern kommt nach dem Trimmen. Das ist die Stelle, an der die Reihenfolge wirklich zählt: eine Zeile, in der nur ein Leerzeichen steht, ist vor dem Trimmen nicht leer. Filterst du zuerst, bleibt sie stehen und taucht später als Eintrag ohne Inhalt wieder auf.

Umformen kommt zuletzt, wenn die Form steht. Vorher weißt du nicht, ob jede Zeile das hergibt, was du herausholen willst.

Nie auf der Rohform arbeiten

Warum nicht auf der Rohform arbeiten
const eingabe = "Ida ; 98";

// Der schnelle Weg, ohne Zwischenschritte.
const teile = eingabe.split(";");
console.log("Name roh:", `[${teile[0]}]`);
console.log("Punkte roh:", teile[1]);

// Sieht in der Console fast richtig aus. Und dann das hier:
console.log("Vergleich:", teile[0] === "Ida");
console.log("Rechnung:", teile[1] + 2);

// Mit Aufräumen stimmt beides.
const name = teile[0].trim();
const punkte = Number(teile[1].trim());
console.log("Vergleich:", name === "Ida");
console.log("Rechnung:", punkte + 2);

// Und das Original steht unverändert da, wie immer.
console.log("Eingabe:", eingabe);

Die Versuchung ist groß, sich das Aufräumen zu sparen. In der Console sieht die Rohform oft richtig aus, denn ein Leerzeichen am Rand sieht man dort nicht.

Auffallen tut es an zwei Stellen. Der Vergleich schlägt fehl: "Ida " === "Ida" ist false, und in einer Ausgabe sehen beide gleich aus. Und die Rechnung liefert Unsinn: aus " 98" + 2 wird der Text " 982", weil das Plus verkettet, sobald eine Seite Text ist. Das ist Lektion 2.7, und hier begegnet es dir zum zweiten Mal.

Einen Ort gibt es, an dem man ein Leerzeichen am Rand doch sieht: die Eingabezeile der Console. Tipp "Ida " ein, und die Antwort ist "Ida " mit den Anführungszeichen drum herum, und zwischen dem a und dem schließenden Zeichen steht sichtbar eine Lücke. Genauso bei " 98" + 2, wo die Antwort " 982" schon durch die Anführungszeichen sagt, dass hier nicht gerechnet wurde. Wenn dir ein Vergleich unerklärlich false liefert, ist das der schnellste Weg, den Übeltäter zu sehen.

Beides sind stille Fehler. Kein TypeError, keine Meldung, nur ein falsches Ergebnis weiter unten. Genau deshalb räumt man auf, bevor man arbeitet, und nicht erst, wenn etwas nicht stimmt.

Und wie immer in diesem Abschnitt: das Original bleibt unangetastet. Du erzeugst eine saubere Fassung daneben und überschreibst nicht das, was hereinkam. Wenn später jemand fragt, was eigentlich eingegeben wurde, hast du es noch.

Aussortieren statt reparieren

Aussortieren statt reparieren
const zeilen = ["Ida;98", "kaputt", "Ben;91", "Mila;keinezahl"];

// Der bequeme Weg: alles umformen und hoffen.
const naiv = zeilen.map((zeile) => {
  const [name, punkte] = zeile.split(";");
  return { name, punkte: Number(punkte) };
});
console.log("Naiv:", naiv);

// Kein Fehler, kein Hinweis, und in den Daten steht Unsinn.
const summeNaiv = naiv.reduce((s, e) => s + e.punkte, 0);
console.log("Summe naiv:", summeNaiv);

// Besser: erst trennen, dann umformen.
const gueltig = zeilen.filter((zeile) => {
  const teile = zeile.split(";");
  return teile.length === 2 && !Number.isNaN(Number(teile[1]));
});
const ungueltig = zeilen.filter((zeile) => !gueltig.includes(zeile));

console.log("Gültig:", gueltig);
console.log("Ungültig:", ungueltig);

Echte Eingaben enthalten Zeilen, die nicht passen. Der naive Weg formt sie trotzdem um und hofft, dass es gutgeht.

Es geht nicht gut, und es kracht auch nicht. Number("keinezahl") ist NaN, und NaN steckt jede Rechnung an: eine einzige kaputte Zeile macht die ganze Summe zu NaN. Ein Feld, das gar nicht da war, wird undefined und taucht später als leerer Text in der Ausgabe auf.

Schau dabei genau hin, wo das NaN steht. In der Console siehst du es auch innerhalb des Objekts, also als {name: "Ida", punkte: NaN}, und die Summe darunter zeigt es noch einmal für sich. Ein einziger solcher Eintrag reicht, und die ganze Rechnung ist hin.

Die Antwort darauf ist trennen, nicht reparieren. Was der Form entspricht, wird umgeformt. Was nicht passt, kommt auf einen zweiten Stapel und wird gemeldet, statt stillschweigend zu einem Nullwert zu werden.

Der Unterschied ist kein Stilfrage. Eine reparierte Zeile sieht hinterher aus wie eine gültige, und niemand erfährt, dass die Eingabe fehlerhaft war. Eine aussortierte Zeile kannst du zurückmelden, und der Mensch davor kann sie korrigieren. In Abschnitt 13 wird daraus eine Fehlermeldung am Formularfeld.

Was belastbar heißt

Die belastbare Form
const zeilen = ["Ida;98", "kaputt", "Ben;91"];

function istGueltig(zeile) {
  const teile = zeile.split(";");
  return teile.length === 2 && !Number.isNaN(Number(teile[1]));
}

function alsEintrag(zeile) {
  const [name, punkte] = zeile.split(";");
  return { name: name.trim(), punkte: Number(punkte.trim()) };
}

const eintraege = zeilen.filter(istGueltig).map(alsEintrag);
const abgelehnt = zeilen.filter((zeile) => !istGueltig(zeile));

console.log("Einträge:", eintraege);
console.log("Abgelehnt:", abgelehnt);

// Ab hier darf sich alles auf die Form verlassen. Kein ?., kein
// Number.isNaN, kein Sonderfall. Genau das ist der Gewinn.
const summe = eintraege.reduce((s, e) => s + e.punkte, 0);
const beste = eintraege.reduce((a, b) => (b.punkte > a.punkte ? b : a));
console.log("Summe:", summe);
console.log("Beste:", beste.name);

Am Ende steht eine Liste von Objekten, und die hat eine Eigenschaft, die den ganzen Aufwand rechtfertigt: jeder Eintrag hat wirklich beide Felder, und punkte ist wirklich eine Zahl.

Das klingt selbstverständlich und ist es nicht. Es gilt nur, weil vorher gefiltert wurde. Der Gewinn zeigt sich in allem, was danach kommt: die Summe braucht kein Number.isNaN, die Suche nach dem besten Eintrag kein ?., keine einzige Funktion dahinter braucht einen Sonderfall.

Diese Grenze ist der eigentliche Inhalt der Lektion. Auf der einen Seite steht alles, was schmutzig sein darf, und dort wird geprüft. Auf der anderen Seite verlässt sich alles auf die Form. Wer diese Grenze nicht zieht, prüft überall ein bisschen und nirgends genug.

Zwei Ausblicke noch. In Abschnitt 9 wird die Prüfung selbst präziser: statt „enthält ein Semikolon” schreibst du ein Muster, das die ganze Zeile beschreibt. Und in Abschnitt 14 lernst du, wie eine Funktion einen Fehler wirft, statt ihn als Rückgabewert zu melden.

Zum Mitnehmen

Belastbar heißt: die nächste Funktion darf sich auf die Form verlassen. Alles, was das kaputtmachen könnte, ist vorher aussortiert und nicht stillschweigend repariert.

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.

Was in dieser Lektion steckt

  • Artikel mit 4 Beispielen zum Ausprobieren

    Steht hier, ohne Konto lesbar.

  • Aufgabe im Editor, direkt im Browser geprüft

    Öffnet sich mit dem Basis Konto.