`Array`-Objekt

Dieser Artikel erklärt das Array-Objekt.

Ich erkläre die praktische Anwendung von Arrays Schritt für Schritt auf leicht verständliche Weise.

YouTube Video

Array-Objekt

Das Array-Objekt in JavaScript ist eine der wichtigsten Strukturen und bildet die Grundlage für alle Arten der Datenverarbeitung. Von grundlegenden Array-Operationen bis hin zu höheren Funktionen für eine effiziente Datenumwandlung gibt es viele nützliche Funktionen, die Sie kennen sollten.

Grundlagen von Arrays

In JavaScript sind Arrays eine grundlegende Datenstruktur, um mehrere Werte gemeinsam zu verwalten. Hier zeigen wir, wie man Arrays erstellt und ihre Elemente mit einfachen Beispielen liest und schreibt.

 1// Create arrays in different ways
 2const arr1 = [1, 2, 3];           // array literal
 3const arr2 = new Array(4, 5, 6);  // Array constructor
 4const arr3 = Array.of(7, 8, 9);   // Array.of
 5
 6console.log("arr1 created with literal.   :", arr1);
 7console.log("arr2 created with constructor:", arr2);
 8console.log("arr3 created with Array.of.  :", arr3);
 9// arr1 created with literal.   : [ 1, 2, 3 ]
10// arr2 created with constructor: [ 4, 5, 6 ]
11// arr3 created with Array.of.  : [ 7, 8, 9 ]
12
13// Access and modify elements
14let first = arr1[0];              // read element
15console.log("First element of arr1:", first);
16// First element of arr1: 1
17
18arr1[1] = 20;                     // modify element
19console.log("arr1 after modifying index 1:", arr1);
20// arr1 after modifying index 1: [ 1, 20, 3 ]
21
22const len = arr1.length;          // get length
23console.log("Length of arr1:", len);
24// Length of arr1: 3
  • Dieser Code zeigt drei Arten, Arrays zu erstellen, wie man Elemente mit Indizes liest und aktualisiert sowie wie man die Länge mit der length-Eigenschaft abruft.
  • Array-Literale sind am gebräuchlichsten und am lesbarsten und werden in alltäglichen Situationen am häufigsten verwendet.

Hinzufügen und Entfernen von Elementen (am Ende oder Anfang)

Mit Arrays können Sie ganz einfach Elemente am Anfang oder Ende hinzufügen oder entfernen. Diese Operationen sind auch beim Implementieren von Strukturen wie Stacks oder Queues nützlich.

 1// Push and pop (stack-like)
 2const stack = [];
 3console.log("Initial stack:", stack);
 4
 5stack.push(1);                    // push 1
 6console.log("After push(1):", stack);
 7
 8stack.push(2);                    // push 2
 9console.log("After push(2):", stack);
10
11const last = stack.pop();         // pop -> 2
12console.log("Popped value:", last);
13console.log("Stack after pop():", stack);
14
15// Unshift and shift (queue-like)
16const queue = [];
17console.log("Initial queue:", queue);
18
19queue.push('a');                  // add to end
20console.log("After push('a'):", queue);
21
22queue.unshift('start');           // add to front
23console.log("After unshift('start'):", queue);
24
25const firstItem = queue.shift();  // remove from front
26console.log("Shifted value:", firstItem);
27console.log("Queue after shift():", queue);
28
29// Initial stack: []
30// After push(1): [ 1 ]
31// After push(2): [ 1, 2 ]
32// Popped value: 2
33// Stack after pop(): [ 1 ]
34
35// Initial queue: []
36// After push('a'): [ 'a' ]
37// After unshift('start'): [ 'start', 'a' ]
38// Shifted value: start
39// Queue after shift(): [ 'a' ]
  • push und pop arbeiten am Ende eines Arrays. Sie eignen sich ideal zur Umsetzung von Stapelstrukturen (Stacks).
  • unshift und shift arbeiten am Anfang eines Arrays. Beachten Sie jedoch, dass Operationen am Anfang alle Elementindizes intern verschieben müssen, was diese zu einer aufwendigeren Operation macht.

Bearbeiten von Elementen in der Mitte (splice und slice)

Wenn Sie Elemente in der Mitte eines Arrays bearbeiten, wählen Sie zwischen splice und slice je nachdem, ob Sie das Originalarray ändern möchten oder nicht. Wenn Sie nur einen Teil eines Arrays extrahieren möchten, verwenden Sie slice; wenn Sie das Array selbst ändern möchten, zum Beispiel Elemente einfügen oder löschen, verwenden Sie splice.

 1// slice (non-destructive)
 2const nums = [0, 1, 2, 3, 4];
 3console.log("Original nums:", nums);
 4
 5const part = nums.slice(1, 4); // returns [1, 2, 3]
 6console.log("Result of nums.slice(1, 4):", part);
 7console.log("nums after slice (unchanged):", nums);
 8
 9// splice (destructive)
10const arr = [10, 20, 30, 40];
11console.log("\nOriginal arr:", arr);
12
13// remove 1 item at index 2, insert 25 and 27
14arr.splice(2, 1, 25, 27);
15console.log("After arr.splice(2, 1, 25, 27):", arr);
16
17// Original nums: [ 0, 1, 2, 3, 4 ]
18// Result of nums.slice(1, 4): [ 1, 2, 3 ]
19// nums after slice (unchanged): [ 0, 1, 2, 3, 4 ]
20
21// Original arr: [ 10, 20, 30, 40 ]
22// After arr.splice(2, 1, 25, 27): [ 10, 20, 25, 27, 40 ]
  • slice extrahiert nur Elemente und ändert das ursprüngliche Array nicht.
  • splice fügt Elemente ein oder entfernt sie und ändert das Array selbst – seien Sie daher besonders vorsichtig bezüglich der Auswirkungen auf das Verhalten.

Iteration (for / for...of / forEach)

Es gibt verschiedene Möglichkeiten, Arrays sequentiell zu verarbeiten. Wählen Sie entsprechend dem Verwendungszweck und Ihrem bevorzugten Programmierstil. Hier sind drei typische Schleifen-Konstrukte.

 1const items = ['apple', 'banana', 'cherry'];
 2console.log("Items:", items);
 3
 4// classic for
 5console.log("\n--- Classic for loop ---");
 6for (let i = 0; i < items.length; i++) {
 7  console.log(`Index: ${i}, Value: ${items[i]}`);
 8}
 9
10// for...of (recommended for values)
11console.log("\n--- for...of loop ---");
12for (const item of items) {
13  console.log(`Value: ${item}`);
14}
15
16// forEach (functional style)
17console.log("\n--- forEach loop ---");
18items.forEach((item, index) => {
19  console.log(`Index: ${index}, Value: ${item}`);
20});
  • Die for-Schleife ist die flexibelste und ermöglicht Indexoperationen sowie eine präzise Kontrolle der Iteration mit break und anderen Anweisungen.
  • for...of bietet eine elegante Möglichkeit, mit Elementwerten umzugehen und ist hinsichtlich Lesbarkeit am ausgewogensten.
  • forEach erlaubt einen funktionalen Programmierstil und eignet sich gut für Operationen mit Nebeneffekten wie Protokollierung oder Datenaktualisierung pro Element. Beachten Sie jedoch, dass Sie break oder continue nicht verwenden können, und es ist nicht für asynchrone Verarbeitung mit await geeignet.

map / filter / reduce — Höhere Funktionen

map, filter und reduce sind höherwertige Funktionen, die häufig beim Transformieren, Filtern oder Aggregieren von Arrays verwendet werden. Da Sie wiederholende Verarbeitung klar ausdrücken können, wird Ihr Code einfach und leicht verständlich.

 1const numbers = [1, 2, 3, 4, 5];
 2console.log("Original numbers:", numbers);
 3
 4// map: transform each item
 5const doubled = numbers.map(n => n * 2);
 6console.log("\nResult of map (n * 2):", doubled);
 7
 8// filter: select items
 9const evens = numbers.filter(n => n % 2 === 0);
10console.log("Result of filter (even numbers):", evens);
11
12// reduce: accumulate to single value
13const sum = numbers.reduce((acc, n) => acc + n, 0);
14console.log("Result of reduce (sum):", sum);
15
16// Original numbers: [ 1, 2, 3, 4, 5 ]
17// Result of map (n * 2): [ 2, 4, 6, 8, 10 ]
18// Result of filter (even numbers): [ 2, 4 ]
19// Result of reduce (sum): 15
  • Diese Methoden ermöglichen es, sich in deklarativem Stil auf das gewünschte Ergebnis zu konzentrieren, was die Lesbarkeit erhöht und unerwünschte Nebeneffekte vermeidet.

find / findIndex / some / every

Hier ein Überblick über Such- und Bedingungsprüfmethoden. Sie sind nützlich, um Elemente zu finden, die bestimmte Bedingungen erfüllen, oder boolesche Prüfungen auf dem Array durchzuführen.

 1const users = [
 2  { id: 1, name: 'Alice' },
 3  { id: 2, name: 'Bob' },
 4  { id: 3, name: 'Carol' }
 5];
 6
 7console.log("Users:", users);
 8
 9// Find the first user whose name is 'Bob'
10const bob = users.find(user => user.name === 'Bob');
11console.log("\nResult of find (name === 'Bob'):", bob);
12
13// Find index of the user whose id is 3
14const indexOfId3 = users.findIndex(user => user.id === 3);
15console.log("Result of findIndex (id === 3):", indexOfId3);
16
17// Check if there exists a user with id = 2
18const hasId2 = users.some(user => user.id === 2);
19console.log("Result of some (id === 2):", hasId2);
20
21// Check if all users have a numeric id
22const allHaveNumericId = users.every(user => typeof user.id === 'number');
23console.log("Result of every (id is number):", allHaveNumericId);
24// Result of find (name === 'Bob'): { id: 2, name: 'Bob' }
25// Result of findIndex (id === 3): 2
26// Result of some (id === 2): true
27// Result of every (id is number): true
  • find gibt das erste Element zurück, das die Bedingung erfüllt.
  • findIndex gibt den Index des Elements zurück, das die Bedingung erfüllt.
  • some gibt true zurück, wenn mindestens ein Element die Bedingung erfüllt.
  • every gibt true zurück, wenn alle Elemente die Bedingung erfüllen.

Alle diese Methoden sind bei der Array-Verarbeitung sehr nützlich. Eine ihrem Zweck entsprechende Verwendung hält Ihren Code klar und übersichtlich.

Sortieren und Vergleichsfunktionen

Arrays werden mit sort sortiert, aber standardmäßig erfolgt der Vergleich als Zeichenkette, was beim Sortieren von Zahlen zu unerwarteten Ergebnissen führen kann.

 1const nums = [10, 2, 33, 4];
 2console.log("Original nums:", nums);
 3
 4// Default sort: compares elements as strings (not suitable for numbers)
 5nums.sort();
 6console.log("\nAfter default sort (string comparison):", nums);
 7
 8// Numeric ascending sort using a compare function
 9nums.sort((a, b) => a - b);
10console.log("After numeric sort (a - b):", nums);
11
12// Sort objects by a property
13const people = [{ age: 30 }, { age: 20 }, { age: 25 }];
14console.log("\nOriginal people:", people);
15
16people.sort((a, b) => a.age - b.age);
17console.log("After sorting people by age:", people);
18
19// After default sort (string comparison): [ 10, 2, 33, 4 ]
20// After numeric sort (a - b): [ 2, 4, 10, 33 ]
21// Original people: [ { age: 30 }, { age: 20 }, { age: 25 } ]
22// After sorting people by age: [ { age: 20 }, { age: 25 }, { age: 30 } ]
  • Beim Sortieren von Zahlen oder Objekten sollten Sie immer eine Vergleichsfunktion angeben, um die gewünschte Reihenfolge zu erreichen.
  • In einer Vergleichsfunktion platziert ein negativer Rückgabewert a vor b, ein positiver Wert placeirt b vor a, und 0 ändert die Reihenfolge nicht.

Array-Kopieren und Unveränderlichkeit (Immutability)

Beim Kopieren von Arrays ist es wichtig, den Unterschied zwischen 'Referenzkopie' und 'flacher Kopie' zu verstehen. Seien Sie besonders vorsichtig, wenn sich Objekte im Array befinden: Bei einer flachen Kopie werden die inneren Objekte gemeinsam verwendet.

Referenzkopie

Wenn Sie ein Array einer anderen Variablen zuweisen, wird das Array selbst nicht dupliziert; stattdessen wird die ‘Referenz’, die auf dasselbe Array zeigt, kopiert.

 1const a = [1, 2, 3];
 2console.log("Original a:", a);
 3
 4// Reference copy; modifying b also affects a
 5const b = a;
 6console.log("\nReference copy b = a:", b);
 7// Reference copy b = a: [ 1, 2, 3 ]
 8
 9b[0] = 100;
10console.log("After modifying b[0] = 100:");
11console.log("a:", a);  // a: [ 100, 2, 3 ] (affected)
12console.log("b:", b);  // b: [ 100, 2, 3 ]
  • Bei einer Referenzkopie werden Änderungen am Inhalt des Arrays mittels der kopierten Variablen auch in der ursprünglichen Variablen, die auf dasselbe Array verweist, sichtbar.

Flache Kopie

Die Verwendung von slice() oder der Spread-Syntax erzeugt eine 'flache Kopie', da nur die Werte dupliziert werden und das Original- sowie das kopierte Array als getrennte Einheiten behandelt werden.

 1const a = [1, 2, 3];
 2console.log("Original a:", a);
 3
 4// Shallow copy (new array)
 5const b = a.slice();
 6console.log("\nShallow copy using slice():", b);
 7// Shallow copy using slice(): [ 100, 2, 3 ]
 8
 9const c = [...a];
10console.log("Shallow copy using spread [...a]:", c);
11// Shallow copy using spread [...a]: [ 100, 2, 3 ]
12
13// Modifying c or d does NOT affect a
14b[1] = 200;
15c[2] = 300;
16console.log("\nAfter modifying b[1] = 200 and c[2] = 300:");
17console.log("a:", a);   // [ 100, 2, 3 ]
18console.log("b:", b);   // [ 100, 200, 3 ]
19console.log("c:", c);   // [ 100, 2, 300 ]
  • Dieser Code zeigt, dass eine durch slice() oder die Spread-Syntax erzeugte flache Kopie eines Arrays das Originalarray nicht beeinflusst.

Flache Kopie und Unveränderlichkeit

Auch wenn Sie ein Array mit einer 'flachen Kopie' duplizieren, kann es zu unbeabsichtigtem Teilen kommen, wenn das Array Objekte enthält.

 1// Shallow copy doesn't clone inner objects
 2const nested = [{ x: 1 }, { x: 2 }];
 3console.log("\nOriginal nested:", nested);
 4// Original nested: [ { x: 1 }, { x: 2 } ]
 5
 6const shallow = nested.slice();
 7console.log("Shallow copy of nested:", shallow);
 8// Shallow copy of nested: [ { x: 1 }, { x: 2 } ]
 9
10// Changing inner object affects both arrays
11shallow[0].x = 99;
12console.log("\nAfter shallow[0].x = 99:");
13console.log("nested:", nested);
14console.log("shallow:", shallow);
15// nested: [ { x: 99 }, { x: 2 } ]
16// shallow: [ { x: 99 }, { x: 2 } ]
  • Dieser Code zeigt, dass bei einer flachen Kopie die inneren Objekte gemeinsam genutzt werden, sodass Änderungen an diesen inneren Objekten sowohl das Originalarray als auch die Kopie betreffen.
  • Wenn Sie unabhängige Daten benötigen, müssen Sie eine 'tiefe Kopie' durchführen, zum Beispiel mit structuredClone() oder per JSON-Konvertierung.

Nützliche Hilfsmethoden

Die folgenden Hilfsmethoden werden beim Arbeiten mit Arrays häufig verwendet. Wenn Sie sie je nach Zweck richtig einsetzen, wird Ihr Code kurz und gut lesbar.

includes

Die Methode includes prüft, ob ein bestimmter Wert in einem Array enthalten ist.

1// includes: check if array contains a value
2const letters = ['a', 'b', 'c'];
3const hasB = letters.includes('b');
4console.log("letters:", letters);  // [ 'a', 'b', 'c' ]
5console.log("letters.includes('b'):", hasB); // true
  • In diesem Code wird die Methode includes verwendet, um prägnant zu bestimmen, ob ein bestimmter Wert im Array vorhanden ist.

concat

Die Methode concat gibt ein neues Array zurück, an dessen Ende das angegebene Array oder die angegebenen Werte angehängt werden, wobei das Originalarray unverändert bleibt.

1// concat: merge arrays without mutation
2const a1 = [1, 2];
3const a2 = [3, 4];
4const combined = a1.concat(a2);
5console.log("a1.concat(a2):", combined); // [ 1, 2, 3, 4 ]
6console.log("a1(unchanged):", a1);       // [ 1, 2 ]
  • Dieser Code zeigt, dass concat nicht destruktiv ist, sodass Sie ein neues Array erzeugen können, während das ursprüngliche erhalten bleibt.

flat

Mit der Methode flat können Sie verschachtelte Arrays einebnen.

1// flat and flatMap: flatten arrays or map + flatten in one step
2const nested = [1, [2, [3]]];
3console.log("nested:", nested); // nested: [ 1, [ 2, [ 3 ] ] ]
4console.log("nested.flat():", nested.flat()); // default depth = 1
5// nested.flat(): [ 1, 2, [ 3 ] ]
  • Dieser Code zeigt das Ergebnis des Einebnens eines Arrays um eine Ebene.
  • Da Sie bei flat die Tiefe angeben können, können Sie Verschachtelungen flexibel nach Bedarf auflösen.

flatMap

Die Methode flatMap wendet eine Transformation auf jedes Element an und flacht anschließend die Ergebnisse automatisch zu einem eindimensionalen Array ab.

1// flat and flatMap: flatten arrays or map + flatten in one step
2const words = ['hello world', 'hi'];
3console.log("words:", words); // [ 'hello world', 'hi' ]
4
5const splitWords = words.flatMap(w => w.split(' '));
6console.log("words.flatMap(w => w.split(' ')):", splitWords);
7// words.flatMap(w => w.split(' ')): [ 'hello', 'world', 'hi' ]
  • Dieser Code zeigt ein Beispiel, bei dem jeder String in einem Array an Leerzeichen getrennt wird und die Ergebnisse kombiniert sowie in ein einziges Array abgeflacht werden.

join

Die Methode join erstellt einen String, indem die Elemente eines Arrays mit einem angegebenen Trennzeichen verknüpft werden.

1// join: combine elements into a string with a separator
2const csv = ['a', 'b', 'c'].join(',');
3console.log("['a', 'b', 'c'].join(','):", csv);  // a,b,c
  • In diesem Code wird die Methode join verwendet, um ein Array in einen durch Kommas getrennten String umzuwandeln.

Häufige Fallstricke

Array-Operationen wirken auf den ersten Blick einfach, aber es gibt einige Punkte, die leicht zu unerwartetem Verhalten führen können. Viele Fallstricke können im Alltag leicht übersehen werden – beachten Sie deshalb die folgenden Punkte, um die Zuverlässigkeit Ihres Codes zu erhöhen.

  • sort() beim Array sortiert standardmäßig per Zeichenkettenvergleich. Um Zahlen korrekt zu sortieren, müssen Sie immer eine Vergleichsfunktion angeben.
  • Das Kopieren von Arrays (über slice, Spread-Syntax usw.) erzeugt nur eine flache Kopie. Wenn Ihre Arrays Objekte enthalten, seien Sie vorsichtig, da die Originaldaten versehentlich verändert werden könnten.
  • splice ist eine destruktive Methode, die das Array direkt verändert, während slice eine nicht-destruktive Methode ist, die das Originalarray nicht verändert. Es ist wichtig, sie entsprechend Ihres Zwecks gezielt einzusetzen.
  • forEach ist nicht geeignet für Schleifen mit asynchroner Verarbeitung unter Verwendung von await. Wenn Sie asynchrone Verarbeitung zuverlässig der Reihe nach ausführen möchten, empfiehlt sich die Verwendung von for...of.

Praktisches Beispiel

Im Folgenden finden Sie ein Beispiel, das Array-Methoden kombiniert, um 'das Gesamtalter aus den Benutzerdaten zu berechnen, diejenigen ab 30 herauszufiltern und eine Namensliste zu erstellen.'.

 1const users = [
 2  { name: 'Alice', age: 28 },
 3  { name: 'Bob', age: 34 },
 4  { name: 'Carol', age: 41 },
 5  { name: 'Dave', age: 19 }
 6];
 7
 8console.log("Users:", users);
 9
10// sum ages
11const totalAge = users.reduce((acc, u) => acc + u.age, 0);
12console.log("\nTotal age of all users:", totalAge);
13// Total age of all users: 122
14
15// filter and map names of users 30 and older
16const namesOver30 = users
17  .filter(u => u.age >= 30)
18  .map(u => u.name);
19
20console.log("Users aged 30 or older (names):", namesOver30);
21// Users aged 30 or older (names): [ 'Bob', 'Carol' ]
  • Durch das Verketten von reduce, filter und map lassen sich Aggregation, Bedingungsextraktion und Transformation von Daten einfach ausdrücken.
  • Eine solche 'Datenverarbeitungspipeline' ist sehr gut lesbar und wird wegen ihrer geringen Seiteneffekte häufig in der Praxis verwendet.

Zusammenfassung

Mit JavaScript-Arrays sind selbst Basisoperationen vielseitig einsetzbar, und durch höhere Funktionen wird Ihr Code prägnanter und ausdrucksstärker. Es gibt viele Aspekte zu verstehen, aber wenn Sie den gezielten Einsatz beherrschen, wird die Datenverarbeitung wesentlich reibungsloser ablaufen.

Sie können den obigen Artikel mit Visual Studio Code auf unserem YouTube-Kanal verfolgen. Bitte schauen Sie sich auch den YouTube-Kanal an.

YouTube Video