Coding Practice / Module 1 · Strings / Problems 1–10

Module 1: String Practice Problems

Ten problems that all boil down to the same thing: walking a string one character (or two pointers) at a time. Reversing, checking palindromes and anagrams, counting letters, and rebuilding text — the same handful of loop patterns come back again and again once you've seen them here. Every problem is solved twice: once in JavaScript, once in Python, with comments on every meaningful line.

Module 1 of 12 Problems 1–10 JS + Python ~45–60 Min

By the end of this module, you'll be able to

  • Reverse, scan and rebuild a string without reaching for a built-in shortcut first
  • Use a frequency map (object/dict) to count characters in a single pass
  • Read the same string algorithm fluently in both JavaScript and Python

1. Problems 1–10

Click a problem to expand it. Each one has a one-line prompt, a short note on the approach, and two fully commented solutions side by side — JavaScript on the left, Python on the right.

P01

Reverse a String

Turn "hello" into "olleh" without a built-in reverse shortcut.

Approach: walk the string backwards from its last index to its first, appending each character to a new result string as you go.

JavaScript
reverse-string.js
// Reverse a string without using the built-in Array.reverse() shortcut,
// so the logic itself is visible.
function reverseString(str) {
  let reversed = ""; // build the result up one character at a time
  for (let i = str.length - 1; i >= 0; i--) {
    reversed += str[i]; // walk backwards from the last character
  }
  return reversed;
}

console.log(reverseString("hello")); // "olleh"
Python
reverse_string.py
def reverse_string(s: str) -> str:
    """Reverse a string by walking it backwards, one character at a time."""
    reversed_str = ""
    # range(len(s) - 1, -1, -1) walks the index backwards, like the JS loop
    for i in range(len(s) - 1, -1, -1):
        reversed_str += s[i]
    return reversed_str
    # Idiomatic one-liner for real code: s[::-1]

print(reverse_string("hello"))  # "olleh"
P02

Check if a String Is a Palindrome

"Was it a car or a cat I saw?" reads the same ignoring case and punctuation.

Approach: strip everything but letters/digits and lowercase it, then use two pointers — one from each end — moving inward and comparing as they go.

JavaScript
is-palindrome.js
// A palindrome reads the same forwards and backwards, e.g. "level".
function isPalindrome(str) {
  const cleaned = str.toLowerCase().replace(/[^a-z0-9]/g, ""); // ignore case/punctuation
  let left = 0;
  let right = cleaned.length - 1;
  while (left < right) {
    if (cleaned[left] !== cleaned[right]) return false; // mismatch found
    left++;
    right--;
  }
  return true; // pointers met without a mismatch
}

console.log(isPalindrome("Was it a car or a cat, I saw?")); // true
Python
is_palindrome.py
import re

def is_palindrome(s: str) -> bool:
    """Two pointers move inward from both ends and compare characters."""
    cleaned = re.sub(r"[^a-z0-9]", "", s.lower())  # ignore case/punctuation
    left, right = 0, len(cleaned) - 1
    while left < right:
        if cleaned[left] != cleaned[right]:
            return False  # mismatch found
        left += 1
        right -= 1
    return True  # pointers met without a mismatch

print(is_palindrome("Was it a car or a cat, I saw?"))  # True
P03

Count Vowels and Consonants

Classify every letter in a sentence into one of two buckets.

Approach: loop over each character, skip anything that isn't a letter, then check membership in a small vowel set to decide which counter to increment.

JavaScript
vowel-consonant-count.js
function countVowelsConsonants(str) {
  const vowels = "aeiouAEIOU";
  let vowelCount = 0;
  let consonantCount = 0;
  for (const ch of str) {
    if (!/[a-zA-Z]/.test(ch)) continue; // skip spaces, digits, punctuation
    if (vowels.includes(ch)) {
      vowelCount++;
    } else {
      consonantCount++;
    }
  }
  return { vowels: vowelCount, consonants: consonantCount };
}

console.log(countVowelsConsonants("Hello World")); // { vowels: 3, consonants: 7 }
Python
vowel_consonant_count.py
def count_vowels_consonants(s: str) -> dict:
    vowels = set("aeiouAEIOU")
    vowel_count = 0
    consonant_count = 0
    for ch in s:
        if not ch.isalpha():  # skip spaces, digits, punctuation
            continue
        if ch in vowels:
            vowel_count += 1
        else:
            consonant_count += 1
    return {"vowels": vowel_count, "consonants": consonant_count}

print(count_vowels_consonants("Hello World"))  # {'vowels': 3, 'consonants': 7}
P04

Character Frequency Count

Build a map of how many times each character appears in a string.

Approach: a frequency map is a plain object/dict — for each character, look up its current count (defaulting to 0) and add one.

JavaScript
char-frequency.js
function charFrequency(str) {
  const freq = {};
  for (const ch of str) {
    freq[ch] = (freq[ch] || 0) + 1; // default to 0 the first time we see ch
  }
  return freq;
}

console.log(charFrequency("banana"));
// { b: 1, a: 3, n: 2 }
Python
char_frequency.py
def char_frequency(s: str) -> dict:
    freq = {}
    for ch in s:
        freq[ch] = freq.get(ch, 0) + 1  # default to 0 the first time we see ch
    return freq

print(char_frequency("banana"))
# {'b': 1, 'a': 3, 'n': 2}
P05

Check if Two Strings Are Anagrams

"listen" and "silent" use exactly the same letters.

Approach: normalize both strings the same way — lowercase, strip non-letters, sort the characters — and compare the results. Two anagrams normalize to an identical string.

JavaScript
are-anagrams.js
function areAnagrams(a, b) {
  const normalize = (s) =>
    s.toLowerCase().replace(/[^a-z0-9]/g, "").split("").sort().join("");
  return normalize(a) === normalize(b); // same letters, same counts, any order
}

console.log(areAnagrams("listen", "silent")); // true
Python
are_anagrams.py
def are_anagrams(a: str, b: str) -> bool:
    def normalize(s: str) -> str:
        letters = [ch for ch in s.lower() if ch.isalnum()]
        return "".join(sorted(letters))  # same letters, same counts, any order

    return normalize(a) == normalize(b)

print(are_anagrams("listen", "silent"))  # True
P06

First Non-Repeating Character

In "swiss", the first letter that appears only once is "w".

Approach: two passes over the string. First, build a frequency map of every character. Second, walk the string in order and return the first character whose count is exactly 1.

JavaScript
first-non-repeating.js
function firstNonRepeatingChar(str) {
  const freq = {};
  for (const ch of str) freq[ch] = (freq[ch] || 0) + 1;
  for (const ch of str) {
    if (freq[ch] === 1) return ch; // first character whose count is exactly 1
  }
  return null; // every character repeats
}

console.log(firstNonRepeatingChar("swiss")); // "w"
Python
first_non_repeating.py
def first_non_repeating_char(s: str) -> str | None:
    freq = {}
    for ch in s:
        freq[ch] = freq.get(ch, 0) + 1
    for ch in s:
        if freq[ch] == 1:  # first character whose count is exactly 1
            return ch
    return None  # every character repeats

print(first_non_repeating_char("swiss"))  # "w"
P07

Remove All Whitespace

Strip every space, tab and newline out of a string.

Approach: build a new string by copying over every character that is not a space, tab or newline.

JavaScript
remove-whitespace.js
function removeWhitespace(str) {
  let result = "";
  for (const ch of str) {
    if (ch !== " " && ch !== "\t" && ch !== "\n") {
      result += ch; // keep everything that isn't a space/tab/newline
    }
  }
  return result;
}

console.log(removeWhitespace("  Hello   World  ")); // "HelloWorld"
Python
remove_whitespace.py
def remove_whitespace(s: str) -> str:
    result = ""
    for ch in s:
        if ch not in (" ", "\t", "\n"):  # keep everything that isn't whitespace
            result += ch
    return result

print(remove_whitespace("  Hello   World  "))  # "HelloWorld"
P08

Title Case a Sentence

"the quick brown fox" becomes "The Quick Brown Fox".

Approach: split the sentence into words, uppercase each word's first letter and lowercase the rest, then join the words back together with spaces.

JavaScript
title-case.js
function toTitleCase(sentence) {
  const words = sentence.split(" ");
  const capitalized = words.map((word) => {
    if (word.length === 0) return word; // guard against double spaces
    return word[0].toUpperCase() + word.slice(1).toLowerCase();
  });
  return capitalized.join(" ");
}

console.log(toTitleCase("the quick brown fox")); // "The Quick Brown Fox"
Python
title_case.py
def to_title_case(sentence: str) -> str:
    words = sentence.split(" ")
    capitalized = []
    for word in words:
        if len(word) == 0:  # guard against double spaces
            capitalized.append(word)
        else:
            capitalized.append(word[0].upper() + word[1:].lower())
    return " ".join(capitalized)

print(to_title_case("the quick brown fox"))  # "The Quick Brown Fox"
P09

Check if a String Is All Digits

"48213" qualifies; "482a3" doesn't.

Approach: an empty string has no digits, so guard against that first. Otherwise, check every character falls between "0" and "9" — strings compare character-by-character, so this works without converting to a number.

JavaScript
is-all-digits.js
function isAllDigits(str) {
  if (str.length === 0) return false; // empty string has no digits
  for (const ch of str) {
    if (ch < "0" || ch > "9") return false; // any non-digit disqualifies it
  }
  return true;
}

console.log(isAllDigits("48213")); // true
console.log(isAllDigits("482a3")); // false
Python
is_all_digits.py
def is_all_digits(s: str) -> bool:
    if len(s) == 0:  # empty string has no digits
        return False
    for ch in s:
        if ch < "0" or ch > "9":  # any non-digit disqualifies it
            return False
    return True
    # Idiomatic one-liner for real code: s.isdigit()

print(is_all_digits("48213"))  # True
print(is_all_digits("482a3"))  # False
P10

Find the Longest Word in a Sentence

In "The crow flew over the mountain", that's "mountain".

Approach: split on spaces, then keep a running "longest so far" — every time a word beats the current record, it becomes the new record.

JavaScript
longest-word.js
function longestWord(sentence) {
  const words = sentence.split(" ");
  let longest = "";
  for (const word of words) {
    if (word.length > longest.length) {
      longest = word; // new record holder
    }
  }
  return longest;
}

console.log(longestWord("The crow flew over the mountain")); // "mountain"
Python
longest_word.py
def longest_word(sentence: str) -> str:
    words = sentence.split(" ")
    longest = ""
    for word in words:
        if len(word) > len(longest):  # new record holder
            longest = word
    return longest

print(longest_word("The crow flew over the mountain"))  # "mountain"

2. Key Takeaways

  • Most string problems reduce to one of three moves: a single forward pass, a two-pointer walk from both ends inward, or a frequency map keyed by character.
  • JavaScript and Python read almost identically once you know the small vocabulary swap: str.lengthlen(s), freq[ch] || 0freq.get(ch, 0), template strings ↔ f-strings.
  • Both languages ship faster built-ins for several of these (split("").reverse().join(""), s[::-1], s.isdigit()) — worth knowing, but writing the loop yourself first is what builds the underlying skill.