Inhalt
Die Fakten:
Plattform: | codewars.com |
Name: | Ranking position |
Level: | 7 kyu |
Sprache: | TypeScript |
Beschreibung:
In some ranking people collects points. The challenge is sort by points and calulate position for every person. But remember if two or more persons have same number of points, they should have same position number and sorted by name (name is unique).
For example: Input structure:
[
{
name: "John",
points: 100,
},
{
name: "Bob",
points: 130,
},
{
name: "Mary",
points: 120,
},
{
name: "Kate",
points: 120,
},
]
[
[
"name" => "John",
"points" => 100,
],
[
"name" => "Bob",
"points" => 130,
],
[
"name" => "Mary",
"points" => 120,
],
[
"name" => "Kate",
"points" => 120,
],
]
Output should be:
[
{
name: "Bob",
points: 130,
position: 1,
},
{
name: "Kate",
points: 120,
position: 2,
},
{
name: "Mary",
points: 120,
position: 2,
},
{
name: "John",
points: 100,
position: 4,
},
]
[
[
"name" => "Bob",
"points" => 130,
"position" => 1,
],
[
"name" => "Kate",
"points" => 120,
"position" => 2,
],
[
"name" => "Mary",
"points" => 120,
"position" => 2,
],
[
"name" => "John",
"points" => 100,
"position" => 4,
],
]
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
Als Erstes das Input-Array sortieren (erst nach Punkten, dann nach Namen).
Schritt 2
Dann können wir durch das sortierte Array loopen.
Schritt 3
Wenn die Punkte der aktuellen Person ungleich der Punkte der vorigen Person sind,
Schritt 4
erhöhen wir die Position (setzen die Position auf i + 1
).
Schritt 5
Dann noch den Position-Key zu jeder Person hinzufügen.
Code
Geil. Übersetzen wir unseren Pseudo-Code in TypeScript:
Lösungsschritte
Meine erste Zeile (inklusive Types):
export type inputMan = {
name: string;
points: number;
};
export type outputMan = {
name: string;
points: number;
position: number;
};
export function ranking(people: inputMan[]): outputMan[] {
Positionsvariable initialisieren:
let position = 1;
Die People sortieren:
const sortedPeople = [...people].sort((a, b) => {
if (a.points === b.points) {
return a.name.localeCompare(b.name);
}
return b.points - a.points;
});
Erst erstellen wir eine Kopie vom Input-Array, damit die Ursprungsdaten unversehrt bleiben. Auf die Kopie wenden wir dann .sort()
an:
Wenn die Punkte von Person a
und Person b
gleich sind, wird nach Buchstaben sortiert. Ansonsten nach den Punkten absteigend.
Alternative => Eine coole kürzere Variante wäre:
const sortedPeople = [...people].sort(
(a, b) => b.points - a.points || a.name.localeCompare(b.name),
);
Was passiert hier?
.sort()
loopt durch das
Array. Wenn wir Zahlen sortieren, ziehen wir die eine Zahl (das nächste Element)
von der anderen Zahl (das aktuelle Element) ab a - b
. Wenn diese Differenz
negativ ist, kommt das zweite Element vor das erste. Wenn 0
rauskommt, heißt
das, beide sind gleich. Es muss also nicht umsortiert werden.
0
ist außerdem falsy
. D.h., in unserer ODER
-Verknüpfung oben wird zum Teil rechts des ODER
-Operators gesprungen. Mit .localeCompare()
werden die Buchstaben verglichen.
return
-Statement brauchen wir nicht mehr, weil wir nur eine einzige Zeile haben (auch wenn prettier
mir das Ding hier mehrzeilig macht...).
Jetzt noch zu jeder Person die Position hinzufügen:
return sortedPeople.map((person, i) => {
const prevPerson = sortedPeople[i - 1];
if (prevPerson) {
if (person.points !== prevPerson.points) position = i + 1;
}
return { ...person, position };
});
}
Dafür bietet sich .map()
an.
Das Ergebnis des Loops können wir direkt mit einem return
davor zurückgeben.
Wir speichern der Übersichtlichkeit halber die vorige Person in einer Variablen.
Da vor der ersten Person keine vorige Person existiert, müssen wir sicherstellen, dass die nächste Aktion nur ausgeführt wird, wenn eine vorige Person existiert.
Nur wenn die aktuelle und die vorige Person verschiedene Punkte haben wird die Positions-Variable auf i + 1
gesetzt (bleibt also ansonsten gleich).
Zum Schluss hängen wir an das person
-Objekt die zugehörige Position an.
Voilá! 💪
Komplettlösung
export type inputMan = {
name: string;
points: number;
};
export type outputMan = {
name: string;
points: number;
position: number;
};
export function ranking(people: inputMan[]): outputMan[] {
let position = 1;
const sortedPeople = [...people].sort((a, b) => {
if (a.points === b.points) {
return a.name.localeCompare(b.name);
}
return b.points - a.points;
});
return sortedPeople.map((person, i) => {
const prevPerson = sortedPeople[i - 1];
if (prevPerson) {
if (person.points !== prevPerson.points) position = i + 1;
}
return { ...person, position };
});
}