Problem overview
The method convertToHackerSpeak receives a string and returns a coded copy of it. Five lowercase letters are replaced by digits: a becomes 4, e becomes 3, i becomes 1, o becomes 0 and s becomes 5. Every other character stays as it is, including spaces.
For "javascript is cool" the result is "j4v45cr1pt 15 c00l". For "become a coder" it is "b3c0m3 4 c0d3r". In "programming is fun" the letters u and n don't change, so the result is "pr0gr4mm1ng 15 fun".
Approach: build a new string character by character
A Scala String is immutable, so you can't overwrite a character in the string you were given. Instead, you build a new string. Start with an empty one, look at each input character in order, and append either its replacement or the character itself. The new string ends up with exactly the same length as the input, with the letters swapped.
Deciding what to append is a chain of questions. Is it an a? Then append 4. If not, is it an e? And so on through i, o and s. If none of them matched, append the character unchanged. Each character takes exactly one path through the chain.
Step by step
- Declare
var solution = "". It must be avar, because each append creates a longer string and stores it back into the variable. - Loop with
for (c <- input). Looping over aStringgives its characters one at a time, each as aChar. - Handle
'a'. Testc == 'a'and append'4'withsolution += '4'. Single quotes make aCharliteral, the same type asc. Double quotes would make aString. - Handle
'e','i','o'and's'. Each one is anotherelse ifon the chain. Anelse ifis tested only when all the conditions before it were false, so only one branch runs per character. - Finish with a plain
else. It runs for every other character and appendscunchanged, which keeps spaces, punctuation, uppercase letters and the other lowercase letters. - Return
solutionafter the loop.
The solution in Scala
class Solution {
def convertToHackerSpeak(input: String): String = {
var solution = ""
for (c <- input) {
if (c == 'a') {
solution += '4'
} else if (c == 'e') {
solution += '3'
} else if (c == 'i') {
solution += '1'
} else if (c == 'o') {
solution += '0'
} else if (c == 's') {
solution += '5'
} else {
solution += c
}
}
return solution
}
}
Language details
+=on aString.solution += '4'meanssolution = solution + '4'. Adding aCharto aStringgives a newStringwith that character at the end. The old string is not modified, which is why the variable has to be avar.CharagainstString.'a'is a character and"a"is a one-character string. Writingc == "a"doesn't compile: the compiler reportsValues of types Char and String cannot be compared with == or !=.- Only lowercase letters change. Case matters:
"Alice SEES"becomes"Al1c3 SEES". The capitalAand the capitals inSEESare different characters fromaands, so they pass through the finalelse. - Order of the chain. The five tests are mutually exclusive, so the order doesn't change the result. Only the final
elsehas to come last.
Tracing a short input
For the input "is a", the variable solution changes like this:
c | Branch taken | solution afterwards |
|---|---|---|
'i' | 'i' | "1" |
's' | 's' | "15" |
' ' | final else | "15 " |
'a' | 'a' | "15 4" |
A match version
The five-way else if chain is the plain way to write this, and it is what the lesson uses. Scala also has pattern matching with match, which expresses "one of several cases" more compactly. The body of the loop can become one expression:
solution += (c match {
case 'a' => '4'
case 'e' => '3'
case 'i' => '1'
case 'o' => '0'
case 's' => '5'
case other => other
})
The last case, case other => other, matches any character not listed before it and gives it back unchanged, exactly like the final else. Wrapped in the same loop, it turns "become a coder" into "b3c0m3 4 c0d3r". Both forms do the same work, so use whichever reads better to you.
Complexity
The loop visits each of the n characters once, so there are n iterations of constant-looking work. The returned string has length n, so the extra space is O(n).
One caveat applies to the time. Each += on a String copies the existing text to make the new, longer string, so the copying alone costs more than a constant per character: the total work for a long input grows roughly with the square of its length. For the short strings in this lesson that is not a concern. For long inputs, a StringBuilder with append avoids the repeated copying and keeps the work proportional to n. The branches stay the same, and only the accumulator changes:
val sb = new StringBuilder
for (c <- "banana") {
if (c == 'a') sb.append('4') else sb.append(c)
}
println(sb.toString)
That fragment prints b4n4n4.
Edge cases and common mistakes
- An empty string. The loop doesn't run and the method returns
"". - Digits already in the input. A
'4'or a'0'in the input is just another character. It goes through the finalelseand stays as it is, so the method never turns a digit back into a letter. - Nothing to replace. A string such as
"fun"comes back unchanged. - Dropping the final
else. Without it, characters that need no change would vanish from the result. - Forgetting that strings are immutable. Calling a method on the input and ignoring the returned string changes nothing.
- Mixing
CharandStringliterals. Use single quotes for the character comparisons and for the appended digits.
Testing
Try "javascript is cool" ("j4v45cr1pt 15 c00l"), "become a coder" ("b3c0m3 4 c0d3r"), "Alice SEES" ("Al1c3 SEES") and "" ("").
