Codewars Lösung | Rainfall


coden
Codewars. Achieve mastery through challenge.
Daniel Kaser|5. März 2024
5 min.

Inhalt

  1. Die Fakten
  2. Beschreibung
  3. Lösung
    1. Pseudo-Code
    2. Code
  4. Feedback

Die Fakten:

Plattform:codewars.com
Name:Rainfall
Level:6 kyu
Sprache:TypeScript

Beschreibung:

dataand data1 are two strings with rainfall records of a few cities for months from January to December. The records of towns are separated by \n. The name of each town is followed by :.

data and towns can be seen in "Your Test Cases:".

Task:

  • function: mean(town, strng) should return the average of rainfall for the city town and the strng data or data1 (In R and Julia this function is called avg).
  • function: variance(town, strng) should return the variance of rainfall for the city town and the strng data or data1.

Examples:

mean("London", data), 51.19(9999999999996) 
variance("London", data), 57.42(833333333374)

Notes:

  • if functions mean or variance have as parameter town a city which has no records return -1 or -1.0 (depending on the language)

  • Don't truncate or round: the tests will pass if abs(your_result - test_result) <= 1e-2 or abs((your_result - test_result) / test_result) <= 1e-6 depending on the language.

  • Shell

    1. Shell tests only variance.

    2. In "function "variance" $1 is "data", $2 is "town".

  • A ref:

  • data and data1 (can be named d0 and d1 depending on the language; see "Sample Tests:") are adapted from:

Quelle: codewars.com

Lösung

Pseudo-Code

Wie immer gibt's reichlich Varianten, hier ist eine meiner.

Erst die Lösungsschritte in Pseudo-Code. Los geht’s:

Lösungsschritte
Schritt 1

Da wir die nackten Regenmengen in beiden Funktionen brauchen, schreibe ich mir als Erstes eine Helfer-Funktion, die diese nackten Daten extrahiert.

Schritt 2

Auf diese Werte greife ich dann in meiner mean-Funktion zu.

Schritt 3

Wenn es keine Werte gibt, wird -1 zurückgegeben.

Schritt 4

Ansonsten geben wir den Durchschnitt zurück.

Schritt 5

Auch in der variance-Funktion greife ich auf die Werte aus der Hilfsfunktion zurück.

Schritt 6

Auch hier wird -1 zurückgegeben, wenn keine Werte existieren.

Schritt 7

Dann berechnen wir die Varianz.

Schritt 8

Die Varianz berechnen wir, indem wir

Schritt 9
  1. die Abweichung, also den Abstand jeden Wertes vom Durchschnitt berechnen und quadrieren
Schritt 10
  1. den Durchschnitt dieser erhaltenen Werte berechnen

Code

Geil. Übersetzen wir unseren Pseudo-Code in TypeScript:

Lösungsschritte
Die erste Zeile meiner Hilfsfunktion um an die nackten Werte zu kommen:
export function getValues(town: string, str: string): number[] | null {
Ich trenne zuerst die Stadt von den Rohdaten:
const [city, data] = str
  .split("\n")
  .filter((line) => line.includes(town))
  .join()
  .split(":");

Dazu erstelle ich mir erst ein Array mit allen Zeilen. Dann sortiere ich die Zeile raus, die meine gesuchte Stadt enthält. Mit .join() wandele ich das Array wieder in einen String um. Diesen String wandele ich dann gleich wieder in ein Array mit 2 Werten um, die durch das ":" getrennt sind. Diese beiden Werte deconstructe ich in die Variablen city und data.

Wenn es die gesuchte Stadt nicht gibt, können wir gleich null zurückgeben:
if (city !== town) return null;
Jetzt kümmern wir uns um die Daten. Wir brauchen nur die reinen Zahlen:
const nakedValues = data
  .replace(/[a-z\s]/gi, "")
  .split(",")
  .map(Number);

Dazu entferne ich mit einer Regex erst mal alle (Klein- und Groß-)Buchstaben und Leerzeichen bzw. Whitespaces. Das was übrig bleibt, sind die Werte kommagetrennt. Mit .split(",") entferne ich diese Kommas und wandele den String in ein Array mit eben diesen Werten um. Danach mache ich aus den String-Werten mit .map(Number) noch Zahlen.

Dann noch die Werte zurückgeben und wir haben unsere getValues-Hilfsfunktion:
  return nakedValues;
}

Weiter gehts mit der mean-Funktion.

Die erste Zeile meiner mean-Funktion:
export function mean(town: string, str: string): number {
Zuerst holen wir uns die nackten Werte aus dem Input:
const values = getValues(town, str);

Dafür können wir einfach unsere Hilfsfunktion callen. Geil!

Wenn es keine Werte (zu der Stadt) gibt, also wenn wir von getValues() z.B. null bekommen, geben wir -1 zurück:
if (!values) return -1;
Jetzt nur noch den Durchschnitt der Werte berechnen:
  return values.reduce((sum, value) => sum + value) / 12;
}

Mit .reduce() summieren wir alle Werte auf und teilen sie danach durch die Anzahl der Werte, also 12. Das Resultat können wir mit einem return davor direkt zurückgeben.

Und zu guter letzt noch unsere variance-Funktion.

Die erste Zeile meiner variance-Funktion:
export function variance(town: string, str: string): number {
Wieder können wir die nackten Werte einfach mit unserer Hilfsfunktion ziehen:
const values = getValues(town, str);
Danach wieder kucken, ob es überhaupt Werte gibt:
if (!values) return -1;
Für die Varianz brauchen wir den Durchschnitt aller Werte:
const average = mean(town, str);

Dafür hatten wir auch schon eine Funktion! Die Werte speichere ich in average, weil mean schon für die Funktion reserviert ist.

Jetzt berechnen wir die Abweichung, also den Abstand jeden Wertes vom Durchschnitt und quadrieren diesen jeweils:
values.map((value) => Math.abs(average - value) ** 2);

Math.abs() gibt uns den absoluten Wert zurück. Auch bekannt als "Betrag". Zu Deutsch: Wenn unser errechnete Wert ein Vorzeichen hat, wird es entfernt.

Aus den erhaltenen Beträgen errechnen wir wieder den Durchschnitt:
      .reduce((sum, squaredDeviation) => sum + squaredDeviation) / 12

Das können wir, wie oben, einfach mit einem drangehangenen .reduce() machen.

Danach noch das Resultat zurückgeben. Ich setze einfach ein return vor unsere letzte Method-Chain und das sieht dann zusammen so aus:
  return values
    .map((value) => Math.abs(average - value) ** 2)
    .reduce((sum, squaredDeviation) => sum + squaredDeviation) / 12
}
Voilá! 💪

Fragen?

Komplettlösung
export function mean(town: string, str: string): number {
  const values = getValues(town, str);
  if (!values) return -1;

  return values.reduce((sum, value) => sum + value) / 12;
}

export function variance(town: string, str: string): number {
  const values = getValues(town, str);
  if (!values) return -1;

  const average = mean(town, str);

  return (
    values
      .map((value) => Math.abs(average - value) ** 2)
      .reduce((sum, squaredDeviation) => sum + squaredDeviation) / 12
  );
}

export function getValues(town: string, str: string): number[] | null {
  const [city, data] = str
    .split("\n")
    .filter((line) => line.includes(town))
    .join()
    .split(":");

  if (city !== town) return null;

  const nakedValues = data
    .replace(/[a-z\s]/gi, "")
    .split(",")
    .map(Number);

  return nakedValues;
}

Feedback

Schreib mir!