51 Übungsaufgaben

  1. Erstellen Sie eine Funktion, die zwei Zahlen als Parameter akzeptiert und ihre Summe zurückgibt.

Lösung:

function sum(a: number, b: number): number {
  return a + b;
}

console.log(sum(5, 3)); // Ausgabe: 8
  1. Erstellen Sie eine Klasse namens Auto, die Eigenschaften marke (String) und modell (String) hat. Fügen Sie einen Konstruktor hinzu, um diese Eigenschaften zu initialisieren, und eine Methode getAutoInfo, die eine Zeichenkette mit den Informationen des Autos zurückgibt.

Lösung:

class Auto {
  constructor(private marke: string, private modell: string) {}

  getAutoInfo(): string {
    return `Marke: ${this.marke}, Modell: ${this.modell}`;
  }
}

const auto = new Auto('BMW', 'X3');
// Ausgabe: "Marke: BMW, Modell: X3"
console.log(auto.getAutoInfo());
  1. Erstellen Sie eine generische Funktion identity, die einen Parameter jeglichen Typs akzeptiert und denselben Wert zurückgibt.

Lösung:

function identity<T>(arg: T): T {
  return arg;
}

// Ausgabe: "hello"
console.log(identity('hello'));
// Ausgabe: 42
console.log(identity(42));
  1. Erstellen Sie eine Interface Person mit den Eigenschaften name und alter. Erstellen Sie dann ein Objekt, das dieses Interface implementiert.

Lösung:

interface Person {
  name: string;
  alter: number;
}

const person: Person = {
  name: 'John',
  alter: 30
};

// Ausgabe: { name: 'John', alter: 30 }
console.log(person);
  1. Erstellen Sie eine Funktion, die ein Array von Zahlen als Parameter akzeptiert und das Array in aufsteigender Reihenfolge sortiert.

Lösung:

function sortNumbers(numbers: number[]): number[] {
  return numbers.sort((a, b) => a - b);
}

// Ausgabe: [1, 3, 5, 6, 9]
console.log(sortNumbers([5, 3, 9, 1, 6]));
  1. Erstellen Sie eine Enumeration für die Tage der Woche und verwenden Sie sie in einem Beispiel.

Lösung:

enum Wochentag {
  Montag,
  Dienstag,
  Mittwoch,
  Donnerstag,
  Freitag,
  Samstag,
  Sonntag
}

// Ausgabe: 5
console.log(Wochentag.Samstag);
  1. Erstellen Sie eine Funktion applyOperation, die zwei Zahlen und eine Funktion akzeptiert. Die Funktion sollte die beiden Zahlen als Parameter an die übergebene Funktion übergeben und das Ergebnis zurückgeben. Testen Sie applyOperation mit einer Funktion, die die Summe von zwei Zahlen berechnet, und einer Funktion, die die Differenz zwischen zwei Zahlen berechnet.

Lösung:

// Typendefinition für eine Funktion, die zwei Zahlen akzeptiert 
// und eine Zahl zurückgibt
type NumberOperation = (a: number, b: number) => number;

function applyOperation(a: number, b: number, operation: NumberOperation): number {
  return operation(a, b);
}

// Lambda-Ausdrücke für Summe und Differenz
let sum: NumberOperation = (a, b) => a + b;
let difference: NumberOperation = (a, b) => a - b;

// Ausgabe: 8
console.log(applyOperation(5, 3, sum));
// Ausgabe: 2
console.log(applyOperation(5, 3, difference));

In diesem Beispiel ist NumberOperation ein Typ, der für eine Funktion steht, die zwei Zahlen als Parameter akzeptiert und eine Zahl zurückgibt. Die Funktion applyOperation verwendet diesen Typ für ihren operation-Parameter, so dass Sie eine Funktion übergeben können, die dieser Signatur entspricht. Die sum und difference Funktionen sind Lambda-Ausdrücke, die diese Signatur erfüllen, und können daher an applyOperation übergeben werden.

  1. Erstellen Sie eine Funktion pruefeKlammerung, die einen String als Parameter akzeptiert und überprüft, ob alle öffnenden Klammern korrekt geschlossen werden. Die Funktion sollte true zurückgeben, wenn die Klammerung korrekt ist, und false sonst.

Lösung:

function pruefeKlammerung(input: string): boolean {
  let klammerCounter = 0;

  for (let char of input) {
    if (char === '(') {
      klammerCounter++;
    } else if (char === ')') {
      klammerCounter--;
      if (klammerCounter < 0) {
        // Schließende Klammer ohne passende öffnende Klammer gefunden
        return false;
      }
    }
  }

  // Klammerung ist korrekt, wenn alle öffnenden Klammern geschlossen wurden
  return klammerCounter === 0;
}

// Ausgabe: true
console.log(pruefeKlammerung('2*(a+5)'));
// Ausgabe: false
console.log(pruefeKlammerung('2*)5+a('));
// Ausgabe: false
console.log(pruefeKlammerung('2*(5+a'));

In dieser Lösung durchläuft die Funktion pruefeKlammerung jeden Charakter im Eingabestring. Wenn sie eine öffnende Klammer findet, erhöht sie einen Zähler. Wenn sie eine schließende Klammer findet, verringert sie den Zähler. Wenn der Zähler zu irgendeinem Zeitpunkt negativ wird (was bedeutet, dass eine schließende Klammer gefunden wurde, bevor eine entsprechende öffnende Klammer gefunden wurde), gibt die Funktion sofort false zurück. Am Ende der Funktion überprüft sie, ob der Zähler null ist (was bedeutet, dass jede geöffnete Klammer geschlossen wurde), und gibt das Ergebnis zurück.

Lösung per Rekursion:

function pruefeKlammerung(input: string, index = 0, count = 0): boolean {
  if (count < 0) {
    // Schließende Klammer ohne passende öffnende Klammer gefunden
    return false;
  }
  
  if (index === input.length) {
    // Alle Zeichen durchlaufen, überprüfen, ob
    // alle öffnenden Klammern geschlossen wurden
    return count === 0;
  }

  if (input[index] === '(') {
    count++;
  } else if (input[index] === ')') {
    count--;
  }
  
  return pruefeKlammerung(input, index + 1, count);
}

// Ausgabe: true
console.log(pruefeKlammerung('2*(a+5)'));
// Ausgabe: false
console.log(pruefeKlammerung('2*)5+a('));
// Ausgabe: false
console.log(pruefeKlammerung('2*(5+a'));

In dieser Lösung durchläuft die Funktion pruefeKlammerung rekursiv jeden Charakter im Eingabestring. Der Basisfall für die Rekursion ist, wenn alle Zeichen durchlaufen wurden (index === input.length), dann gibt die Funktion true zurück, wenn alle geöffneten Klammern geschlossen wurden (d.h., count === 0), und false sonst. Wenn der Zähler zu irgendeinem Zeitpunkt negativ wird (was bedeutet, dass eine schließende Klammer gefunden wurde, bevor eine entsprechende öffnende Klammer gefunden wurde), gibt die Funktion sofort false zurück.

Vereinfachte Lösung per Rekursion:

function pruefeKlammerung(input: string, count = 0): boolean {
  if (count < 0 || (input.length === 0 && count !== 0)) {
    // Schließende Klammer ohne passende öffnende Klammer 
    // gefunden oder am Ende nicht alle Klammern geschlossen
    return false;
  }
  
  if (input.length === 0 && count === 0) {
    // Alle Zeichen durchlaufen, alle öffnenden Klammern 
    // wurden geschlossen
    return true;
  }

  const firstChar = input[0];
  const restString = input.slice(1);

  if (firstChar === '(') {
    return pruefeKlammerung(restString, count + 1);
  } else if (firstChar === ')') {
    return pruefeKlammerung(restString, count - 1);
  } else {
    return pruefeKlammerung(restString, count);
  }
}

// Ausgabe: true
console.log(pruefeKlammerung('2*(a+5)'));
// Ausgabe: false
console.log(pruefeKlammerung('2*)5+a('));
// Ausgabe: false
console.log(pruefeKlammerung('2*(5+a'));

In dieser überarbeiteten Version wird der aktuelle String in firstChar (das erste Zeichen des aktuellen Strings) und restString (der Rest des Strings ohne das erste Zeichen) aufgeteilt. Dann wird firstChar überprüft und restString zusammen mit dem aktualisierten Zähler count an die nächste Rekursionsebene übergeben. Diese Strategie macht den Code einfacher und leichter zu verstehen, da sie keine separate Indexvariable benötigt.

Aufgabe 9: Implementieren Sie eine generische SafeArray<T> Klasse in TypeScript, die nur Elemente vom Typ T aufnehmen kann. Die Klasse sollte die Methoden push, pop, get und size enthalten. Implementieren Sie zusätzlich eine Methode isEmpty, die true zurückgibt, wenn das Array leer ist und false sonst.

class SafeArray<T> {
  private elements: T[] = [];

  push(element: T): void {
    this.elements.push(element);
  }

  pop(): T | undefined {
    return this.elements.pop();
  }

  get(index: number): T | undefined {
    return this.elements[index];
  }

  size(): number {
    return this.elements.length;
  }

  isEmpty(): boolean {
    return this.size() === 0;
  }
}

Aufgabe 10: Erstellen Sie eine TypeScript-Funktion flatten, die ein verschachteltes Array beliebiger Tiefe (also ein Array, das andere Arrays enthalten kann, die wiederum andere Arrays enthalten können, usw.) nimmt und ein flaches Array zurückgibt, das alle Elemente des Eingabe-Arrays in der gleichen Reihenfolge enthält.

function flatten(input: any[]): any[] {
  return input.reduce((accumulator, current) => 
    accumulator.concat(Array.isArray(current) 
        ? flatten(current) : current), []);
}

Aufgabe 11: Implementieren Sie in TypeScript das Observer Pattern. Erstellen Sie eine Subject-Klasse mit den Methoden registerObserver, unregisterObserver und notifyObservers. Implementieren Sie auch eine Observer-Interface mit einer update-Methode.

interface Observer {
  update(): void;
}

class Subject {
  private observers: Observer[] = [];

  registerObserver(observer: Observer): void {
    this.observers.push(observer);
  }

  unregisterObserver(observer: Observer): void {
    const observerIndex = this.observers.indexOf(observer);
    if (observerIndex > -1) {
      this.observers.splice(observerIndex, 1);
    }
  }

  notifyObservers(): void {
    for (let observer of this.observers) {
      observer.update();
    }
  }
}

Aufgabe 12: Implementieren Sie eine Funktion pipe, die eine Liste von Funktionen akzeptiert und eine Funktion zurückgibt, die, wenn sie aufgerufen wird, die Ausgabe jeder Funktion in der Liste als Eingabe für die nächste Funktion verwendet.

function pipe(functions: Function[]): Function {
  return function(input: any) {
    return functions.reduce((accumulator, currentFunction) => currentFunction(accumulator), input);
  }
}

Aufgabe 13: Implementieren Sie eine generische Queue<T> Klasse in TypeScript, die die Methoden enqueue, dequeue und size enthält. enqueue sollte ein Element zur Queue hinzufügen, dequeue sollte das älteste Element entfernen und zurückgeben, und size sollte die Anzahl der Elemente in der Queue zurückgeben.

class Queue<T> {
  private elements: T[] = [];

  enqueue(element: T): void {
    this.elements.push(element);
  }

  dequeue(): T | undefined {
    return this.elements.shift();
  }

  size(): number {
    return this.elements.length;
  }
}

Aufgabe 14: Implementieren Sie eine Funktion deepEqual, die zwei Objekte als Eingabe nimmt und true zurückgibt, wenn sie strukturell identisch sind, und false sonst.

function deepEqual(object1: any, object2: any): boolean {
  if (object1 === object2) {
    return true;
  }

  if (typeof object1 !== 'object' || object1 === null || typeof object2 !== 'object' || object2 === null) {
    return false;
  }

  let keys1 = Object.keys(object1);
  let keys2 = Object.keys(object2);

  if (keys1.length !== keys2.length) {
    return false;
  }

  for (let key of keys1) {
    if (!keys2.includes(key) || !deepEqual(object1[key], object2[key])) {
      return false;
    }
  }

  return true;
}