Start learning free

LESSON 142 OF 348 · LOOPS & GLOBAL VARIABLES

Hacker Speak in TypeScript: Replacing Characters in O(n)

Practice Read the full explanation

Given a string, write a function that returns a coded (h4ck3r 5p34k) version of the string.

In order to work properly, the function should replace all "a"s with 4, "e"s with 3, "i"s with 1, "o"s with 0, and "s"s with 5.

Example 1:

Input:  "javascript is cool"

Output: "j4v45cr1pt 15 c00l"

Example 2:

Input:  "programming is fun"

Output: "pr0gr4mm1ng 15 fun"

Example 3:

Input:  "become a coder"

Output: "b3c0m3 4 c0d3r"
Hints:

We have to iterate through every character of input, check for each scenario and treat it accordingly:
  • Is it an 'a'? If so, replace with '4'. Otherwise:
  • Is it an 'e'? If so, replace with '3'. Otherwise:
  • Is it an 'i'? If so, replace with '1'. Otherwise:
  • Is it an 'o'? If so, replace with '0'. Otherwise:
  • Is it an 's'? If so, replace with '5'. Otherwise, keep it as it is.
Remember that strings are immutable. How does that affect us? How can we put this strategy into code?

Strings are immutable means you cannot directly change a character in a string like this: input[4] = '4'. This means we have to create a new string where we will store the solution as we create it. How can we translate these two hints in code?

Let's create a new string named solution, initially empty, where we will push all the encoded characters of input. We will use a for loop and a character variable c to iterate through input and treat the scenarios inside the loop:
solution = ""
for each c in 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


We have a for loop that performs exactly n iterations (n is the length of input), thus the Time Complexity is O(n).
The extra space we use is the extra string solution. Its length will be n in the end, thus the Extra Space used is also O(n).
STEP 1 OF 9
    Ctrl + Enter to check

    TYPESCRIPT · LOOPS & GLOBAL VARIABLES

    Hacker Speak in TypeScript, explained

    Problem overview

    The method convertToHackerSpeak receives a string, input, and returns a coded version. Five 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.

    For "javascript is cool" the result is "j4v45cr1pt 15 c00l": the two a letters become 4, the s becomes 5, the i becomes 1, and the o pair becomes 00. For "become a coder" the result is "b3c0m3 4 c0d3r".

    Approach: build a new string character by character

    Strings in TypeScript are immutable: you can't change a character in place, and the compiler rejects an assignment such as input[4] = "4" with the message Index signature in type 'String' only permits reading. So the method builds a second string. It starts empty, visits every character of input in order, and appends either the replacement digit or the original character. At the end it returns the new string.

    For each character exactly one thing is appended: the checks form a chain, and the first one that matches decides. A character that matches none of the five letters falls through to the last branch and is copied unchanged, which covers spaces, punctuation, digits and every other letter.

    Here is the start of a trace for "become a coder". The first character, b, matches none of the five letters, so solution becomes "b". Next, e matches the second test, so solution becomes "b3". Then c is copied, giving "b3c", and o becomes 0, giving "b3c0". The space between the words is copied like any other non-matching character. The order of the five tests does not change the result, because a character can equal only one of the five letters.

    Step by step

    1. Declare solution as an empty string, with let, because += assigns a new value each time. With const, the first append would be an error.
    2. Loop over input with for...of. The constant c holds one character per pass. TypeScript has no separate character type, so c is a string of length one.
    3. Handle "a". If c === "a", append "4".
    4. Handle "e" with an else if. It is checked only when the first test failed, and it appends "3".
    5. Handle "i", appending "1".
    6. Handle "o", appending "0".
    7. Handle "s", appending "5".
    8. Finish the chain with else. Any other character is appended unchanged.
    9. Return solution after the loop, when every character has been processed.

    The solution in TypeScript

    class Solution {
        convertToHackerSpeak(input: string): string {
            let solution: string = "";
            for (const c of 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;
        }
    }
    

    A few TypeScript points:

    • Immutability. solution += "4" doesn't change the existing string. It creates a new one and stores it in solution. The code reads like appending, and the engine handles the cost well, so for a task of this size it is the natural way to build a string.
    • for...of over a string visits one character at a time, in order. for...in would give the indices instead.
    • Characters beyond the basic set. for...of walks a string by code point, so an emoji counts as one pass of the loop even though it occupies two positions in length. For "a😀b" the loop runs 3 times while length is 4. Such characters are copied unchanged by the final else, which is the behaviour you want.
    • Case matters. "A" is a different string from "a", so capital letters are copied unchanged. The statement lists only lowercase letters.
    • Use === for comparison, as in the rest of the course. It compares values without converting types.
    • Annotations are erased. input: string and : string help the compiler and disappear in the JavaScript.

    A table-driven version

    The five-branch chain is the most direct way to write the lesson. Once you're comfortable with it, you can see that the five branches differ only in data, and an object can hold the data instead:

    const codes: Record<string, string> = { a: "4", e: "3", i: "1", o: "0", s: "5" };
    
    function convert(text: string): string {
        let result = "";
        for (const c of text) {
            result += codes[c] ?? c;
        }
        return result;
    }
    

    Here codes[c] is undefined for a character that has no entry, and the ?? operator then uses c instead. It gives the same results: convert("javascript is cool") returns "j4v45cr1pt 15 c00l", and convert("become a coder") returns "b3c0m3 4 c0d3r". Adding a sixth letter means adding one entry rather than one more branch. The lesson asks for the explicit chain, so submit that one.

    Complexity

    Time: O(n), where n is the length of input. Each character is visited once and checked against at most five letters, which is a constant amount of work.

    Space: O(n). The result string has the same length as the input.

    Edge cases and common mistakes

    • The empty string. The loop does not run, and the method returns "".
    • Using a chain of separate if statements instead of else if. A character could then be appended twice: the final else would also fire for letters that were already replaced.
    • Forgetting the final else. Characters that aren't replaced would be dropped from the result.
    • Trying to modify the input. Assigning to input[i] is a compile error and can't change the string. Build a new one instead.
    • Replacing capital letters. "Apple" becomes "Appl3", not "4ppl3". Only lowercase e is replaced.
    • Returning inside the loop, which would stop after the first character.

    Testing

    • "javascript is cool" gives "j4v45cr1pt 15 c00l".
    • "programming is fun" gives "pr0gr4mm1ng 15 fun".
    • "become a coder" gives "b3c0m3 4 c0d3r".
    • "" gives "".
    Keep going with TypeScriptAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free