Inhalt
Die Fakten:
Plattform: | codewars.com |
Name: | Rainfall |
Level: | 6 kyu |
Sprache: | TypeScript |
Beschreibung:
data
and 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 citytown
and thestrng
data
ordata1
(In R and Julia this function is calledavg
). - function:
variance(town, strng)
should return the variance of rainfall for the citytown
and thestrng
data
ordata1
.
Examples:
mean("London", data), 51.19(9999999999996)
variance("London", data), 57.42(833333333374)
Notes:
-
if functions
mean
orvariance
have as parametertown
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
orabs((your_result - test_result) / test_result) <= 1e-6
depending on the language. -
Shell
-
Shell tests only variance.
-
In "function "variance" $1 is "data", $2 is "town".
-
-
A ref:
-
data
anddata1
(can be namedd0
andd1
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
- die Abweichung, also den Abstand jeden Wertes vom Durchschnitt berechnen und quadrieren
Schritt 10
- 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á! 💪
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;
}