Card 1 / 10
Return the string with its characters in reverse order.
s |
result | exercises |
|---|---|---|
"abc" |
"cba" |
ordinary word |
"" |
"" |
nothing to reverse |
"aπb" |
"bπa" |
emoji survives intact |
[...s] spreads a string into an array of its characters. Prefer it over s.split("") here: spreading walks whole code points, so an emoji stays one element instead of splitting into two broken halves.
[...s].reverse().join("") β reverse() flips the array in place, and join("") glues the characters back together with no separator.
function reverseString(s: string): string {
// Return s with its characters in reverse order.
return [...s].reverse().join("");
}Card 2 / 10
Count how many vowels (a, e, i, o, u) appear in the string, ignoring case.
s |
result | exercises |
|---|---|---|
"Hello World" |
3 |
mixed case |
"AEIOU" |
5 |
every vowel, uppercase |
"xyz" |
0 |
no vowels |
"" |
0 |
empty string |
Deal with case once, up front, instead of testing both cases at every character.
for (const ch of s.toLowerCase()) walks the string one character at a time. "aeiou".includes(ch) asks whether ch occurs anywhere inside "aeiou" β a compact stand-in for a set membership test. Count the hits.
function countVowels(s: string): number {
// Count the vowels a, e, i, o, u in s, case-insensitively.
let n = 0;
for (const ch of s.toLowerCase()) {
if ("aeiou".includes(ch)) n++;
}
return n;
}Card 3 / 10
Capitalize the first letter of each space-separated word and lowercase the rest.
s |
result | exercises |
|---|---|---|
"the QUICK brown" |
"The Quick Brown" |
the rest of each word is lowercased |
"a b" |
"A B" |
adjacent spaces make an empty word |
"" |
"" |
empty string |
s.split(" ") cuts on every single space, so "a b" yields ["a", "", "b"] β an empty word you must leave alone rather than index into.
Map each word to w[0].toUpperCase() + w.slice(1).toLowerCase(), where slice(1) is everything from index 1 onward. Guard the empty word with w ? ... : w, then join(" ") to restore the spaces.
function titleCase(s: string): string {
// Capitalize the first letter of each space-separated word and lowercase the rest.
return s
.split(" ")
.map((w) => (w ? w[0].toUpperCase() + w.slice(1).toLowerCase() : w))
.join(" ");
}Card 4 / 10
Decide whether the string reads the same forwards and backwards, ignoring case and any non-letter characters.
s |
result | exercises |
|---|---|---|
"Racecar" |
true |
case ignored |
"A man, a plan, a canal: Panama" |
true |
spaces and punctuation ignored |
"hello" |
false |
not a palindrome |
"" |
true |
empty reads the same either way |
Normalize first: reduce the string to just its letters in a single case. Once that is done the check is a plain equality.
s.toLowerCase().replace(/[^a-z]/g, "") strips it down β [^a-z] matches any character that is not a lowercase letter, and the g flag makes replace remove every match rather than only the first. Lowercasing first is what makes a-z sufficient. Then compare the result against [...cleaned].reverse().join("").
function isPalindrome(s: string): boolean {
// Ignore case and consider letters only. Is s a palindrome?
const cleaned = s.toLowerCase().replace(/[^a-z]/g, "");
return cleaned === [...cleaned].reverse().join("");
}Card 5 / 10
If the string is longer than n, return its first n characters followed by β¦. Otherwise return it unchanged β equal length counts as unchanged.
s |
n |
result | exercises |
|---|---|---|---|
"hello world" |
5 |
"helloβ¦" |
longer than n, so it is cut |
"hello" |
5 |
"hello" |
exactly n is unchanged |
"hi" |
5 |
"hi" |
shorter than n |
s.slice(0, n) returns the characters from index 0 up to but not including n β exactly the first n of them. A single ternary on s.length > n covers both branches, and note the β¦ is one extra character added on top of the n you kept.
function truncate(s: string, n: number): string {
// If s is longer than n, return the first n characters plus "β¦". Otherwise return s unchanged.
return s.length > n ? s.slice(0, n) + "β¦" : s;
}Card 6 / 10
Count the words in the string. Words are separated by whitespace; leading, trailing, or repeated spaces must not count as words.
s |
result | exercises |
|---|---|---|
"hello world" |
2 |
plain case |
" spaced out " |
2 |
padding and runs of spaces ignored |
"one" |
1 |
single word |
"" |
0 |
empty string |
s.trim() removes leading and trailing whitespace, which disposes of the padding. Splitting on the regex /\s+/ β one or more whitespace characters β then treats a whole run of spaces as a single separator.
One case breaks the pattern: "".split(/\s+/) returns [""], length 1, not an empty array. Check for the empty trimmed string and return 0 before splitting.
function countWords(s: string): number {
// Count words separated by whitespace. Leading, trailing, and repeated spaces don't create empty words.
const trimmed = s.trim();
return trimmed === "" ? 0 : trimmed.split(/\s+/).length;
}Card 7 / 10
Uppercase only the first character of the string and leave the rest unchanged.
s |
result | exercises |
|---|---|---|
"hello" |
"Hello" |
ordinary word |
"already Capitalized" |
"Already Capitalized" |
the rest is untouched |
"a" |
"A" |
single character |
"" |
"" |
empty string |
Take the first character with s[0] and the remainder with s.slice(1) β everything from index 1 to the end β uppercase the first, and concatenate the two.
On an empty string s[0] is undefined, and calling .toUpperCase() on that throws, so guard the empty case first: s === "" ? s : ....
function capitalize(s: string): string {
// Uppercase the first character; leave the rest unchanged. Empty string stays empty.
return s === "" ? s : s[0].toUpperCase() + s.slice(1);
}Card 8 / 10
Return the string concatenated n times. n is zero or more.
s |
n |
result | exercises |
|---|---|---|---|
"ab" |
3 |
"ababab" |
ordinary repeat |
"ab" |
1 |
"ab" |
one copy is the string itself |
"x" |
0 |
"" |
zero copies |
"" |
5 |
"" |
nothing to repeat |
Strings carry this operation themselves β no loop and no array needed.
s.repeat(n) returns n copies of s joined end to end, and s.repeat(0) is "". Accumulating out += s in a for loop is the manual alternative if you want to see the mechanics, but the built-in is what to reach for.
function repeatString(s: string, n: number): string {
// Return s concatenated n times. n is >= 0; n = 0 yields "".
return s.repeat(n);
}Card 9 / 10
Return an object mapping each character in the string to the number of times it appears.
s |
result | exercises |
|---|---|---|
"aab" |
{ a: 2, b: 1 } |
repeats counted |
"xx" |
{ x: 2 } |
one distinct character |
"" |
{} |
empty string, empty map |
The return type Record<string, number> describes an object whose keys are strings and whose values are numbers β here, character to count. Start from an empty one, {}, and fill it as you walk the string.
freq[ch] = (freq[ch] ?? 0) + 1 seeds and increments in one line: ?? evaluates to its right-hand side only when the left is null or undefined, so a character seen for the first time starts from 0.
function charFrequency(s: string): Record<string, number> {
// Return a map from each character in s to how many times it appears.
const freq: Record<string, number> = {};
for (const ch of s) freq[ch] = (freq[ch] ?? 0) + 1;
return freq;
}Card 10 / 10
Decide whether two strings are anagrams β the same letters in a different order. Compare letters only, ignoring case, spaces, and punctuation.
a |
b |
result | exercises |
|---|---|---|---|
"listen" |
"silent" |
true |
same letters reordered |
"Dormitory" |
"Dirty Room" |
true |
case and spaces ignored |
"hello" |
"world" |
false |
different letters |
"" |
"" |
true |
two empty strings |
Two strings are anagrams exactly when their letters, sorted, are identical. So normalize each one the same way and compare the two results.
s.toLowerCase().replace(/[^a-z]/g, "") drops everything that is not a lowercase letter, the g flag replacing every match. Strings have no sort, so spread to an array first: [...cleaned].sort().join("") orders the characters and rejoins them.
function isAnagram(a: string, b: string): boolean {
// Are a and b anagrams? Compare letters only, case-insensitively; ignore spaces and punctuation.
const norm = (s: string) =>
[...s.toLowerCase().replace(/[^a-z]/g, "")].sort().join("");
return norm(a) === norm(b);
}