
Inhalt
Die Fakten:
| Plattform: | codewars.com |
| Name: | Range Extraction |
| Level: | 4 kyu |
| Sprache: | JavaScript |
Beschreibung:
A format for expressing an ordered list of integers is to use a comma separated list of either
- individual integers
- or a range of integers denoted by the starting integer separated from the end integer in the range by a dash, '-'. The range includes all integers in the interval including both endpoints. It is not considered a range unless it spans at least 3 numbers. For example "12,13,15-17"
Complete the solution so that it takes a list of integers in increasing order and returns a correctly formatted string in the range format.
Example:
solution([-10, -9, -8, -6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20]);
// returns "-10--8,-6,-3-1,3-5,7-11,14,15,17-20"
solution([-10, -9, -8, -6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20])
# returns "-10--8,-6,-3-1,3-5,7-11,14,15,17-20"
solution([-10, -9, -8, -6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20])
# returns "-10--8,-6,-3-1,3-5,7-11,14,15,17-20"
solution([-10, -9, -8, -6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20])
# returns "-10--8,-6,-3-1,3-5,7-11,14,15,17-20"
Solution.rangeExtraction(new int[] {-10, -9, -8, -6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20})
# returns "-10--8,-6,-3-1,3-5,7-11,14,15,17-20"
RangeExtraction.Extract(new[] {-10, -9, -8, -6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20});
# returns "-10--8,-6,-3-1,3-5,7-11,14,15,17-20"
RangeExtraction.Extract({-10, -9, -8, -6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20});
# returns "-10--8,-6,-3-1,3-5,7-11,14,15,17-20"
range_extraction({-10, -9, -8, -6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20});
// returns "-10--8,-6,-3-1,3-5,7-11,14,15,17-20"
range_extraction((const []){-10, -9, -8, -6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20}, 23);
/* returns "-10--8,-6,-3-1,3-5,7-11,14,15,17-20" */
nums: dd -6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20
mov rdi, nums
mov rsi, 20
call range_extraction
; RAX <- `-10--8,-6,-3-1,3-5,7-11,14,15,17-20\0`
rangeextraction([-10 -9 -8 -6 -3 -2 -1 0 1 3 4 5 7 8 9 10 11 14 15 17 18 19 20])
# returns "-10--8,-6,-3-1,3-5,7-11,14,15,17-20"
solution(List(-10, -9, -8, -6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20))
// "-10--8,-6,-3-1,3-5,7-11,14,15,17-20"
(solution '(-10 -9 -8 -6 -3 -2 -1 0 1 3 4 5 7 8 9 10 11 14 15 17 18 19 20))
; returns "-10--8,-6,-3-1,3-5,7-11,14,15,17-20"
solution([-10, -9, -8, -6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20])
// returns '-10--8,-6,-3-1,3-5,7-11,14,15,17-20'
Rangeextraction
xs = [-10, -9, -8, -6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11,
14, 15, 17, 18, 19, 20]
res = "-10--8,-6,-3-1,3-5,7-11,14,15,17-20"
Courtesy of rosettacode.org
Quelle: codewars.com
Lösung
Pseudo-Code
Es gibt wie immer viele Varianten, hier ist eine meiner.
Erst die Lösungsschritte in Pseudo-Code. Los geht’s:
Lösungsschritte
Schritt 1
Als Erstes brauchen wir 2 Variablen, genauer Arrays. Eine um unser Endergebnis zu speichern und eine zum Zwischenspeichern der aktuellen potentiellen Range
Schritt 2
Dann loopen wir durch die Zahlen des Input-Arrays
Schritt 3
In jeder Iteration berechnen wir den Abstand der aktuellen zur nächsten Zahl
Schritt 4
Wenn dieser Abstand 1 ist, können wir die aktuelle Zahl ruhigen Gewissens an unser Zwischenspeicher-Array hängen
Schritt 5
Wenn der Abstand nicht 1 ist, prüfen wir ob unser Zwischenspeicher-Array leer ist
Schritt 6
Wenn es nicht leer ist, haben wir gerade die letzte Zahl der aktuellen Range und hängen sie noch ans Zwischenspeicher-Array an
Schritt 7
Dann prüfen wir, ob die aktuelle Range im Zwischenspeicher-Array 3 oder mehr Zahlen beeinhaltet
Schritt 8
Wenn ja, können wir die erste und die letzte Zahl der aktuellen Range, getrennt mit einem Bindestrich an unser Endergebnis-Array hängen
Schritt 9
Ansonsten hängen wir nur die beiden Zahlen an, die im Zwischenspeicher-Array sind
Schritt 10
Nachdem wir eine Range hatten, dürfen wir nicht vergessen, das Zwischenspeicher-Array wieder zu leeren
Schritt 11
Wenn der Abstand nicht 1 war und das Zwischenspeicher-Array aktuell leer ist, hängen wir die Zahl wie sie ist an das Endergebnis-Array
Schritt 12
Nach dem Loop noch Endergebnis komma-getrennt ausgeben, fertig!
Code
Geil. Übersetzen wir unseren Pseudo-Code in JavaScript:
Lösungsschritte
Meine erste Zeile (ich habe list in nums umbenannt):
function solution(nums) {
Zuerst unsere beiden Arrays für Endergebnis und zum Zwischenspeichern einer potentiellen aktuellen Range:
const output = [];
let currRange = [];
Dann der Loop durch die Zahlen des Input-Arrays:
for (let i = 0; i < nums.length; i++) {
const currNum = nums[i];
const nextNum = nums[i + 1] ?? Infinity;
Die aktuell und die nächste Zahl speichere ich mir der Übersichtlichkeit halber jeweils in einer Variablen zwischen.
Sollte die nächste Zahl null oder undefined, also nicht existent sein, setze ich sie auf Unendlich. Damit ist sie in jedem Fall ‘pseudo’-größer als die aktuelle Zahl.
Da sie 0 sein kann und 0 falsy ist, kann ich hier nicht einfach das logische ODER (||) verwenden, sondern verwende den Nullish coalescing operator (??).
Dann berechnen wir den Abstand zwischen der aktuellen und der nächsten Zahl:
const distance = Math.abs(currNum - nextNum);
Den reinen Abstand zwischen den beiden Zahlen berechnen wir mit dem Betrag ihrer Differenz (Math.abs()). Wir streichen also quasi ein potentielles (negatives) Vorzeichen.
Dann prüfen wir, ob der Abstand 1 ist:
if (distance === 1) {
currRange.push(currNum);
continue;
}
Dann können wir die aktuelle Zahl an das Zwischenspeicher-Array anhängen.
Mit continue beenden wir die aktuelle Iteration und springen direkt zur nächsten Zahl. Ohne dass der restliche Code in der For-Schleife ausgeführt wird.
Ich bin ein großer Fan dieses Keywords, denn so kann man sich jede Menge unübersichtlicher if-else-Konstruktionen sparen. Mehr dazu findest du hier.
Wenn der Abstand nicht 1 ist, prüfen wir, ob unser Zwischenspeicher-Array gerade leer ist:
if (currRange.length) {
currRange.push(currNum);
Wenn also beides zutrifft hieße das, dass die aktuelle Zahl auch die letzte Zahl der aktuellen Range ist. Die können wir auch noch ans aktuelle Zwischenspeicher-Array anhängen.
Dann müssen wir noch prüfen, ob die aktuelle Range lang genug ist, um als Range definiert werden zu können:
if (currRange.length >= 3) output.push(currRange[0] + "-" + currNum);
Wenn ja, können wir die erste Zahl der Range und die letzte (also die aktuelle) verbunden mit einem - an unser Ergebnis-Array anhängen.
Wenn nicht, hängen wir nur die erste und die letzte Zahl ohne - an:
else output.push(currRange[0], currNum);
Nicht vergessen die aktuelle Range, also das Zwischenspeicher-Array zurückzusetzen:
currRange = [];
continue;
}
Wenn der Abstand nicht 1 ist und auch das Zwischenspeicher-Array leer ist, hängen wir unsere aktuelle Zahl schnöde an das Ergebnis-Array:
output.push(currNum);
}
Nach dem Loop nur noch das Ergebnis-Array in einen String umwandeln und zurückgeben:
return output.join(",");
}
Voilá! 💪
Komplettlösung
function solution(nums) {
const output = [];
let currRange = [];
for (let i = 0; i < nums.length; i++) {
const currNum = nums[i];
const nextNum = nums[i + 1] ?? Infinity;
const distance = Math.abs(currNum - nextNum);
if (distance === 1) {
currRange.push(currNum);
continue;
}
if (currRange.length) {
currRange.push(currNum);
if (currRange.length >= 3) output.push(currRange[0] + "-" + currNum);
else output.push(currRange[0], currNum);
currRange = [];
continue;
}
output.push(currNum);
}
return output.join(",");
}