Zum Inhalt
wannacode

Zweidimensionale Arrays

Kurz gesagt: Ein zweidimensionales Array (Matrix) ist in JavaScript ein Array von Arrays: Jedes Element des äußeren Arrays ist eine Zeile, und verschachtelte Schleifen gehen durch alle Werte.

Problem

Drei Wochen, drei Arrays

Ein Café notiert, wie viele Kaffees es an jedem Werktag verkauft hat. Für eine Woche ist das Array aus der letzten Lektion genau das Richtige. Für drei Wochen legst du drei Arrays an:

const week1 = [42, 38, 51, 47, 60];
const week2 = [45, 40, 49, 52, 63];
const week3 = [39, 44, 50, 48, 58];
const tuesday = week1[1] + week2[1] + week3[1];
console.log(`Dienstag: ${tuesday}`);
Ausgabe
Dienstag: 122

Jetzt fragt die Chefin, wie viel an allen Dienstagen des letzten Jahres verkauft wurde. Das sind 52 Wochen, also 52 Arrays und eine Summe mit 52 Summanden. Und eine Schleife kann nicht durch week1, week2, week3 gehen: Das sind verschiedene Namen, keine Indizes.

Die Daten sind eine Tabelle: Die Wochen sind Zeilen, die Tage sind Spalten. Du brauchst ein Array, das eine Tabelle ist.

Die Idee

Ein Array von Arrays: Zeile und Spalte

Eine Tabelle ist in JavaScript ein Array von Arrays: Jedes Element des äußeren Arrays ist eine ganze Zeile, und eine Zeile ist ein gewöhnliches Array mit Zahlen. So ein Array heißt zweidimensional, oft auch Matrix.

const sales = [
[42, 38, 51, 47, 60],
[45, 40, 49, 52, 63],
[39, 44, 50, 48, 58],
];
console.log(sales[1][0]);
console.log(`${sales.length} Wochen, ${sales[0].length} Tage`);
Ausgabe
45
3 Wochen, 5 Tage
  • Jedes innere [ … ] ist eine Zeile. Wir schreiben sie untereinander, damit man die Tabelle sieht.
  • sales[1][0] ist die Zeile mit Index 1 (die zweite Woche) und die Spalte mit Index 0 (Montag). Zuerst kommt die Zeile, dann die Spalte. Auch hier zählt man ab 0.
  • sales.length ist die Anzahl der Zeilen. sales[0].length ist die Länge der ersten Zeile, also die Anzahl der Spalten.

sales[1] ist eine ganze Zeile, ein gewöhnliches Array mit 5 Zahlen. Deshalb schreibst du zwei getrennte Klammern [1][0]: Zuerst holst du die Zeile, dann die Zahl darin.

const sales = [
[42, 38, 51, 47, 60],
[45, 40, 49, 52, 63],
[39, 44, 50, 48, 58],
];
console.log(sales[1]);
console.log(sales);
Ausgabe
[ 45, 40, 49, 52, 63 ]
[
[ 42, 38, 51, 47, 60 ],
[ 45, 40, 49, 52, 63 ],
[ 39, 44, 50, 48, 58 ]
]

Eine leere Tabelle 52 × 5 voller Nullen baust du Zeile für Zeile: 52-mal fügst du eine neue Zeile aus 5 Nullen hinzu.

const table = [];
for (let week = 0; week < 52; week++) {
table.push(new Array(5).fill(0));
}
table[0][1] = 7;
console.log(table.length, table[0].length, table[0][1], table[1][1]);
Ausgabe
52 5 7 0
Genauer, für Neugierige
  • Da die Zeilen eigene Arrays sind, können sie unterschiedlich lang sein. So eine Tabelle heißt verzweigt (jagged):
const shifts = [
[8, 8],
[6, 6, 6],
[12],
];
console.log(shifts[1].length, shifts[2].length);
Ausgabe
3 1

In ihr ist sales[0].length nicht die Länge jeder Zeile. Deshalb ist es in einer verschachtelten Schleife sicherer, sales[row].length zu schreiben: die Länge der Zeile, durch die du gerade gehst.

  • Bau die leere Tabelle nicht mit new Array(3).fill(new Array(5).fill(0)). Dann zeigen alle drei Plätze auf ein und dieselbe Zeile, wie die zwei Namen eines Arrays aus der letzten Lektion. Eine Änderung in einer Zeile sieht man in allen:
const table = new Array(3).fill(new Array(5).fill(0));
table[0][1] = 7;
console.log(table);
Ausgabe
[ [ 0, 7, 0, 0, 0 ], [ 0, 7, 0, 0, 0 ], [ 0, 7, 0, 0, 0 ] ]

Sieh es dir an

Die ganze Tabelle auf dem Bildschirm

Um durch jede Zelle zu gehen, brauchst du zwei Schleifen, eine in der anderen. Verschachtelte Schleifen kennst du schon von den Sternchen: Die äußere wählt die Zeile, die innere geht durch die Spalten dieser Zeile.

for (let week = 0; week < sales.length; week++) {
let line = "";
for (let day = 0; day < sales[0].length; day++) {
line += String(sales[week][day]).padStart(5);
}
console.log(line);
}
Ausgabe
42 38 51 47 60
45 40 49 52 63
39 44 50 48 58

Die äußere Schleife läuft 3-mal, einmal für jede Woche. In jeder ihrer Runden läuft die innere 5-mal. Insgesamt 15 Zellen, in der Reihenfolge, in der du eine Tabelle liest: von links nach rechts, von oben nach unten.

String(x).padStart(5) macht aus der Zahl einen Text und füllt ihn links mit Leerzeichen auf 5 Zeichen auf. So stehen die Spalten genau untereinander. Die Zeile wird in line gesammelt, und console.log(line) gibt sie nach der inneren Schleife aus.

Probier es aus

Welche Zelle?

Im Notenbuch ist jede Zeile ein Schüler und jede Spalte ein Fach.

Was gibt das Programm aus?

const grades = [
[5, 3, 4, 6],
[3, 4, 6, 5],
[6, 2, 5, 4],
];
console.log(grades[1][2]);
Antwort

Die Idee

Summen pro Zeile und pro Spalte

Wie viele Kaffees wurden in jeder Woche verkauft? Das ist die Summe jeder Zeile. Die Summe beginnt für jede neue Zeile bei 0, deshalb steht total innerhalb der äußeren Schleife:

for (let week = 0; week < sales.length; week++) {
let total = 0;
for (let day = 0; day < sales[0].length; day++) {
total += sales[week][day];
}
console.log(`Woche ${week + 1}: ${total}`);
}
Ausgabe
Woche 1: 238
Woche 2: 249
Woche 3: 239

const sales = [
[42, 38, 51, 47, 60],
[45, 40, 49, 52, 63],
[39, 44, 50, 48, 58],
];
for (let week = 0; week < sales.length; week++) {
let total = 0;
for (let day = 0; day < sales[0].length; day++) {
total += sales[week][day];
}
console.log(`Woche ${week + 1}: ${total}`);
}
Schritt für Schritt, in Worten
  1. Die Tabelle: 3 Wochen (Zeilen) mit je 5 Tagen (Spalten).
  2. week = 0: total beginnt wieder bei 0.
  3. Die innere Schleife geht durch die Tage: 42 + 38 + 51 + 47 + 60 = 238.
  4. Sie gibt „Woche 1: 238“ aus.
  5. week = 1: total beginnt wieder bei 0.
  6. Die innere Schleife geht durch die Tage: 45 + 40 + 49 + 52 + 63 = 249.
  7. Sie gibt „Woche 2: 249“ aus.
  8. week = 2: total beginnt wieder bei 0.
  9. Die innere Schleife geht durch die Tage: 39 + 44 + 50 + 48 + 58 = 239.
  10. Sie gibt „Woche 3: 239“ aus.

Wie viel wurde am Montag verkauft, am Dienstag und so weiter? Das ist die Summe jeder Spalte. Die Tabelle bleibt dieselbe, nur die Reihenfolge der Schleifen ändert sich: Außen steht der Tag, innen stehen die Wochen. Du schreibst es auf der nächsten Karte fertig.

Welche Schleife außen steht, entscheidet, was du addierst. Die Indizes in sales[week][day] wandern nicht: Zuerst kommt immer die Zeile.

Probier es aus

Summe pro Tag

Die äußere Schleife geht durch die Tage, die innere durch die Wochen. Was gehört in die Lücke?

const days = ["Mo", "Di", "Mi", "Do", "Fr"];
for (let day = 0; day < sales[0].length; day++) {
  let total = 0;
  for (let week = 0; week < sales.length; week++) {
    total += sales[][day];
  }
  console.log(`${days[day]}: ${total}`);
}

Probier es aus

Finde den Fehler

Der Saal hat 3 Reihen mit je 5 Plätzen: 1 ist belegt, 0 ist frei. Es gibt 7 freie Plätze, aber das Programm sagt 5. In welcher Zeile steckt der Fehler?

Wähle die Zeile mit dem Fehler.

Die Idee

Reservierung: Menschen zählen ab 1

Ein Kinobesucher will Reihe 3, Platz 2. Für ihn ist die erste Reihe die Reihe 1. Für das Array ist sie die Reihe 0. Deshalb ziehst du von den Zahlen, die ein Mensch eingibt, 1 ab, bevor du sie als Indizes verwendest:

const row = Number(await ask(""));
const seat = Number(await ask(""));
if (hall[row - 1][seat - 1] === 1) {
console.log("Der Platz ist belegt.");
} else {
hall[row - 1][seat - 1] = 1;
console.log(`Gebucht: Reihe ${row}, Platz ${seat}`);
}
Eingabe
3
2
Ausgabe
Gebucht: Reihe 3, Platz 2
Eingabe
1
2
Ausgabe
Der Platz ist belegt.

hall ist der Saal von der vorigen Karte, und die Zeilen für ask() und rl.close() sind wie in der Lektion „Eingabe und Ausgabe“. Eine Zelle änderst du wie eine gewöhnliche Variable: hall[row - 1][seat - 1] = 1;.

Genauer, für Neugierige
  • Wenn jemand Reihe 7 will, ist hall[6] gleich undefined, und undefined[1] bricht das Programm mit TypeError ab. Will jemand Platz 10 in einer Reihe, die es gibt, kommt kein Fehler: hall[2][9] ist undefined, und die Zuweisung hängt einen neuen Platz ans Ende der Reihe. Die Reihe hat jetzt 10 Plätze:
const hall = [
[1, 1, 0, 0, 1],
[0, 1, 1, 1, 0],
[0, 0, 0, 1, 1],
];
try {
console.log(hall[7 - 1][2 - 1]);
} catch (error) {
console.log(`${error.name}: ${error.message}`);
}
hall[3 - 1][10 - 1] = 1;
console.log(hall[2].length);
Ausgabe
TypeError: Cannot read properties of undefined (reading '1')
10

Prüf deshalb die Zahlen, bevor du sie verwendest: row >= 1 && row <= hall.length, und dasselbe für den Platz.

  • Hier steht jede Zahl in einer eigenen Zeile. Wie du eine ganze Zeile mit Zahlen auf einmal liest (0 1 1 0), siehst du in der nächsten Lektion über Text.

Übung

Rechnen mit einer Tabelle

Löse die Aufgaben in deinem Editor. Schreib die Tabellen direkt in den Code.

1. Diagonalen und Zeilen. Finde für die Tabelle 2 × 2 unten die Summe der Hauptdiagonale (von oben links nach unten rechts), die Summe der anderen Diagonale und die Summen der ersten und der zweiten Zeile. Auf der Hauptdiagonale sind der Index der Zeile und der Index der Spalte gleich. Auf der anderen Diagonale ist die Spalte n - 1 - row.

Die Tabelle
1 2
3 4
Ausgabe
Hauptdiagonale: 5
Nebendiagonale: 5
Erste Zeile: 3
Zweite Zeile: 7

2. Dreh die Tabelle um. Die Verkäufe sind nach Wochen (Zeilen) und Tagen (Spalten) notiert. Die Buchhaltung will sie nach Tagen und Wochen. Erzeuge eine neue Tabelle, in der die Zeilen zu Spalten werden. Das nennt man Transponieren. Die neue Tabelle hat die Maße Spalten × Zeilen, und die Zelle [r][c] wandert nach [c][r].

Die Tabelle
42 38 51
45 40 49
Ausgabe
42 45
38 40
51 49

Übung

Labyrinthe und ein Schatz

3. Wie viele Wege gibt es? Das Labyrinth ist eine Tabelle: 0 ist ein Durchgang, 1 ist eine Wand. Du startest in der oberen linken Ecke und kommst zur unteren rechten, wobei du dich nur nach rechts oder nach unten bewegst. Wie viele verschiedene Wege gibt es?

Das Labyrinth
0 0 0 0
0 1 0 0
0 0 0 1
1 0 0 0
Ausgabe
Wege: 3
Tipp

Leg eine zweite Tabelle ways mit derselben Größe an. Schreib hinein, auf wie viele Arten man jede Zelle erreicht. Den Start erreicht man auf 1 Art. Eine Wand auf 0 Arten. Jede andere Zelle erreicht man entweder von oben oder von links, deshalb ist die Anzahl die Summe aus der Zelle darüber und der Zelle links davon (falls es sie gibt). Füll die Tabelle Zeile für Zeile aus, von oben nach unten. Die Antwort steht in der letzten Zelle.

4. Der kürzeste Weg. Eine schwere Aufgabe. Jetzt darfst du dich in alle vier Richtungen bewegen: nach oben, nach unten, nach links und nach rechts. Mit wie vielen Schritten kommst du mindestens von der oberen linken zur unteren rechten Ecke? Gibt es keinen Weg, gib „Kein Weg“ aus.

Das Labyrinth
0 0 0 0 0
1 1 1 1 0
0 0 0 0 0
0 1 1 1 1
0 0 0 0 0
Ausgabe
Kürzester Weg: 16 Schritte
Tipp

Schick eine „Welle“ vom Start los. Leg eine Tabelle dist voller -1 an (diese Zelle haben wir noch nicht erreicht) und setz 0 an den Start. Dann wiederhole: Finde alle Zellen, deren dist gleich dem aktuellen Schritt ist, und schreib bei ihren freien Nachbarn, die wir noch nicht erreicht haben, Schritt + 1 hinein. Die Welle wächst in jeder Runde um einen Schritt. Hör auf, wenn sie das Ziel erreicht oder wenn sich nichts mehr geändert hat (dann gibt es keinen Weg). Die vier Richtungen sind praktisch als zwei Arrays: const dr = [-1, 1, 0, 0]; und const dc = [0, 0, -1, 1];. Der Nachbar in Richtung k liegt in Zeile r + dr[k] und Spalte c + dc[k].

Weitere Aufgaben

  • Finde den Schatz. Ein Spiel auf einer Karte, die eine Tabelle ist: 0 ist ein Weg, 1 ist ein Hindernis, 2 ist der Schatz. Der Spieler startet in der oberen linken Ecke. Gib bei jedem Zug die Karte aus (P für den Spieler, # für ein Hindernis, . für einen Weg) und lies einen Befehl als Zahl ein: 8 nach oben, 2 nach unten, 4 nach links, 6 nach rechts (wie auf dem Ziffernblock). Führt der Zug aus der Karte hinaus oder in ein Hindernis, bleibt der Spieler stehen. Wenn er auf den Schatz tritt, gib „Du hast den Schatz in … Zügen gefunden!“ aus.
  • Die Woche mit den meisten Verkäufen. Finde für die Tabelle sales heraus, welche Woche die stärkste war und welcher Wochentag im Durchschnitt der stärkste ist.
  • Freie Plätze nebeneinander. Finde für den Saal hall eine Reihe, in der mindestens 3 freie Plätze nebeneinander liegen, und gib die Nummer der Reihe und des ersten Platzes aus.

Test

Teste dich

1. Was ist sales[1] in der Tabelle mit den Verkäufen?
2. Die Tabelle sales hat 3 Wochen (Zeilen) und 5 Tage (Spalten). Was liefert sales[0].length?
3. Welche ist die letzte Zelle einer Tabelle mit 3 Zeilen und 5 Spalten?
4. Du willst die Summe einer Spalte (ein Tag über alle Wochen). Welcher Index ändert sich in der inneren Schleife?

Erstellt von Ivo Gergov im Rahmen der Kampagne von Evo Tech für zugängliche Programmierausbildung ·