String Challenges
String manipulation is one of the most tested areas in JavaScript interviews. These challenges cover common patterns: reversal, character frequency, transformation, and validation.
Challenge: Reverse a String
Write a function that reverses a string without using the built-in .reverse() method directly on the string.
function reverseString(str) {
return str.split("").reverse().join("");
}
// Without .reverse():
function reverseStringManual(str) {
let result = "";
for (let i = str.length - 1; i >= 0; i--) {
result += str[i];
}
return result;
}
console.log(reverseString("hello")); // "olleh"
console.log(reverseStringManual("JavaScript")); // "tpircSavaJ"
"olleh"
"tpircSavaJ"
Challenge: Check Palindrome
Return true if the string reads the same forwards and backwards. Ignore spaces, punctuation, and case.
function isPalindrome(str) {
const cleaned = str.toLowerCase().replace(/[^a-z0-9]/g, "");
return cleaned === cleaned.split("").reverse().join("");
}
console.log(isPalindrome("racecar")); // true
console.log(isPalindrome("A man a plan a canal Panama")); // true
console.log(isPalindrome("hello")); // false
true
true
false
Challenge: Count Vowels
Count the number of vowels (a, e, i, o, u) in a string (case-insensitive).
function countVowels(str) {
return (str.match(/[aeiou]/gi) || []).length;
}
console.log(countVowels("JavaScript")); // 3
console.log(countVowels("rhythm")); // 0
console.log(countVowels("Hello World")); // 3
3
0
3
Challenge: Capitalize Every Word
Capitalize the first letter of every word in a string (title case).
function capitalizeWords(str) {
return str
.split(" ")
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
.join(" ");
}
console.log(capitalizeWords("hello world")); // "Hello World"
console.log(capitalizeWords("the quick brown fox")); // "The Quick Brown Fox"
"Hello World"
"The Quick Brown Fox"
Challenge: Find Most Frequent Character
Return the character that appears most frequently in a string. If there's a tie, return the first one.
function mostFrequentChar(str) {
const freq = {};
for (const char of str) {
freq[char] = (freq[char] || 0) + 1;
}
return Object.entries(freq).reduce((max, curr) =>
curr[1] > max[1] ? curr : max
)[0];
}
console.log(mostFrequentChar("aabbcccdd")); // "c"
console.log(mostFrequentChar("javascript")); // "a"
"c"
"a"
Array Challenges
Array manipulation tests your knowledge of iteration, higher-order functions, and algorithmic thinking. Master these patterns and you'll handle the majority of interview array questions.
Challenge: Flatten a Nested Array
Flatten an arbitrarily deeply nested array into a single-level array.
// Modern approach
function flatten(arr) {
return arr.flat(Infinity);
}
// Recursive approach (classic interview answer)
function flattenRecursive(arr) {
return arr.reduce((acc, val) =>
Array.isArray(val) ? acc.concat(flattenRecursive(val)) : acc.concat(val), []);
}
const nested = [1, [2, [3, [4, [5]]]]];
console.log(flatten(nested)); // [1, 2, 3, 4, 5]
console.log(flattenRecursive(nested)); // [1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
Challenge: Find Duplicates
Return an array of all duplicate values in an array.
function findDuplicates(arr) {
const seen = new Set();
const duplicates = new Set();
for (const item of arr) {
if (seen.has(item)) duplicates.add(item);
else seen.add(item);
}
return [...duplicates];
}
console.log(findDuplicates([1, 2, 3, 2, 4, 3, 5])); // [2, 3]
console.log(findDuplicates(["a", "b", "a", "c"])); // ["a"]
[2, 3]
["a"]
Challenge: Rotate Array
Rotate an array to the right by k positions.
function rotateArray(arr, k) {
const n = arr.length;
const steps = k % n; // handle k > n
return [...arr.slice(n - steps), ...arr.slice(0, n - steps)];
}
console.log(rotateArray([1, 2, 3, 4, 5], 2)); // [4, 5, 1, 2, 3]
console.log(rotateArray([1, 2, 3], 4)); // [3, 1, 2]
[4, 5, 1, 2, 3]
[3, 1, 2]
Challenge: Chunk Array
Split an array into chunks of a given size.
function chunkArray(arr, size) {
const chunks = [];
for (let i = 0; i < arr.length; i += size) {
chunks.push(arr.slice(i, i + size));
}
return chunks;
}
console.log(chunkArray([1,2,3,4,5,6,7], 3)); // [[1,2,3],[4,5,6],[7]]
console.log(chunkArray([1,2,3,4], 2)); // [[1,2],[3,4]]
[[1,2,3],[4,5,6],[7]]
[[1,2],[3,4]]
Challenge: Group Anagrams
Given an array of strings, group anagrams together.
function groupAnagrams(words) {
const map = new Map();
for (const word of words) {
const key = word.split("").sort().join("");
if (!map.has(key)) map.set(key, []);
map.get(key).push(word);
}
return [...map.values()];
}
console.log(groupAnagrams(["eat","tea","tan","ate","nat","bat"]));
// [["eat","tea","ate"], ["tan","nat"], ["bat"]]
[["eat","tea","ate"], ["tan","nat"], ["bat"]]
Number Challenges
Challenge: FizzBuzz
Print numbers 1–100. For multiples of 3 print "Fizz", multiples of 5 print "Buzz", multiples of both print "FizzBuzz".
function fizzBuzz(n = 100) {
for (let i = 1; i <= n; i++) {
if (i % 15 === 0) console.log("FizzBuzz");
else if (i % 3 === 0) console.log("Fizz");
else if (i % 5 === 0) console.log("Buzz");
else console.log(i);
}
}
fizzBuzz(15);
// 1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz
Challenge: Fibonacci Sequence
Return the first n numbers of the Fibonacci sequence. Implement both recursive and iterative approaches.
// Iterative (efficient)
function fibonacci(n) {
const seq = [0, 1];
for (let i = 2; i < n; i++) {
seq.push(seq[i - 1] + seq[i - 2]);
}
return seq.slice(0, n);
}
// Recursive with memoization
function fibMemo(n, memo = {}) {
if (n <= 1) return n;
if (memo[n]) return memo[n];
memo[n] = fibMemo(n - 1, memo) + fibMemo(n - 2, memo);
return memo[n];
}
console.log(fibonacci(8)); // [0, 1, 1, 2, 3, 5, 8, 13]
console.log(fibMemo(10)); // 55
[0, 1, 1, 2, 3, 5, 8, 13]
55
Challenge: Check Prime Number
Return true if a number is prime.
function isPrime(n) {
if (n < 2) return false;
if (n === 2) return true;
if (n % 2 === 0) return false;
for (let i = 3; i <= Math.sqrt(n); i += 2) {
if (n % i === 0) return false;
}
return true;
}
console.log(isPrime(2)); // true
console.log(isPrime(17)); // true
console.log(isPrime(18)); // false
console.log(isPrime(1)); // false
true
true
false
false
Object Challenges
Challenge: Deep Clone an Object
Clone an object so that nested modifications do not affect the original.
function deepClone(obj) {
if (obj === null || typeof obj !== "object") return obj;
if (Array.isArray(obj)) return obj.map(deepClone);
return Object.fromEntries(
Object.entries(obj).map(([k, v]) => [k, deepClone(v)])
);
}
const original = { a: 1, b: { c: 2, d: [3, 4] } };
const clone = deepClone(original);
clone.b.c = 99;
console.log(original.b.c); // 2 (untouched)
console.log(clone.b.c); // 99
2
99
Challenge: Pick / Omit Keys from Object
Implement pick(obj, keys) and omit(obj, keys) utilities.
const pick = (obj, keys) =>
Object.fromEntries(keys.filter(k => k in obj).map(k => [k, obj[k]]));
const omit = (obj, keys) =>
Object.fromEntries(Object.entries(obj).filter(([k]) => !keys.includes(k)));
const user = { id: 1, name: "Alice", email: "alice@example.com", password: "secret" };
console.log(pick(user, ["id", "name"])); // { id: 1, name: "Alice" }
console.log(omit(user, ["password", "email"])); // { id: 1, name: "Alice" }
{ id: 1, name: "Alice" }
{ id: 1, name: "Alice" }
Challenge: Group Array of Objects by Property
Group an array of objects by a specified property key.
function groupBy(arr, key) {
return arr.reduce((groups, item) => {
const groupKey = item[key];
if (!groups[groupKey]) groups[groupKey] = [];
groups[groupKey].push(item);
return groups;
}, {});
}
const people = [
{ name: "Alice", dept: "Engineering" },
{ name: "Bob", dept: "Marketing" },
{ name: "Carol", dept: "Engineering" },
{ name: "Dave", dept: "Marketing" },
];
console.log(groupBy(people, "dept"));
// {
// Engineering: [{ name: "Alice"... }, { name: "Carol"... }],
// Marketing: [{ name: "Bob"... }, { name: "Dave"... }]
// }
Algorithm Challenges
Challenge: Binary Search
Implement binary search on a sorted array. Return the index of the target, or -1 if not found. Time complexity: O(log n).
function binarySearch(arr, target) {
let left = 0;
let right = arr.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (arr[mid] === target) return mid;
if (arr[mid] < target) left = mid + 1;
else right = mid - 1;
}
return -1;
}
const sorted = [1, 3, 5, 7, 9, 11, 13, 15];
console.log(binarySearch(sorted, 7)); // 3
console.log(binarySearch(sorted, 10)); // -1
3
-1
Challenge: Two-Sum Problem
Given an array of integers and a target sum, return the indices of the two numbers that add up to the target. Each input has exactly one solution.
function twoSum(nums, target) {
const seen = new Map(); // value -> index
for (let i = 0; i < nums.length; i++) {
const complement = target - nums[i];
if (seen.has(complement)) {
return [seen.get(complement), i];
}
seen.set(nums[i], i);
}
return [];
}
console.log(twoSum([2, 7, 11, 15], 9)); // [0, 1]
console.log(twoSum([3, 2, 4], 6)); // [1, 2]
[0, 1]
[1, 2]
Challenge: Valid Parentheses
Given a string containing only (, ), {, }, [, ], determine if the input is valid (every open bracket has a matching close bracket in the correct order).
function isValidParentheses(s) {
const stack = [];
const pairs = { ")": "(", "}": "{", "]": "[" };
for (const char of s) {
if ("([{".includes(char)) {
stack.push(char);
} else {
if (stack.pop() !== pairs[char]) return false;
}
}
return stack.length === 0;
}
console.log(isValidParentheses("()[]{}")); // true
console.log(isValidParentheses("([{}])")); // true
console.log(isValidParentheses("([)]")); // false
console.log(isValidParentheses("{[]")); // false
true
true
false
false
Challenge Reference Table
| Challenge | Category | Key Technique | Time Complexity |
|---|---|---|---|
| Reverse String | String | split/reverse/join | O(n) |
| Palindrome | String | Two-pointer / regex | O(n) |
| Most Frequent Char | String | Frequency map | O(n) |
| Find Duplicates | Array | Set | O(n) |
| Group Anagrams | Array | Sort + Map | O(n·k log k) |
| Binary Search | Algorithm | Divide and conquer | O(log n) |
| Two Sum | Algorithm | Hash map | O(n) |
| Valid Parentheses | Algorithm | Stack | O(n) |
Practice Strategy
Work through these challenges in this order:
- Attempt each challenge for 10–15 minutes without looking at the solution.
- Study the solution and understand the algorithm, not just the code.
- Re-implement from memory the next day.
- Identify the underlying pattern (hash map, stack, two pointers, etc.).
- Apply that pattern to a new, similar problem.
Bonus Challenges
Once you're comfortable with the above, try these harder variations:
- Longest substring without repeating characters (sliding window)
- Merge two sorted arrays in O(n) time
- Find the k-th largest element in an array
- Implement a LRU (Least Recently Used) cache
FAQ
How should I approach a coding challenge I've never seen?
Clarify the problem, identify examples and edge cases, choose a brute-force approach first, then optimize. Talking through your reasoning aloud shows the interviewer how you think.
Should I use built-in methods like .sort() in interviews?
Yes, unless the interviewer specifically asks you to implement from scratch. Using built-ins demonstrates practical knowledge. Mention the underlying complexity (.sort() is O(n log n)).
What's the most important data structure to know for JS interviews?
The hash map (Map/Object) is the most versatile. A huge number of O(n) solutions require trading space for time using a hash map. Master it deeply.
How do I get faster at coding challenges?
Consistency beats intensity. Twenty minutes of daily practice for four weeks produces more improvement than a 14-hour weekend cram session.
Is it okay to ask questions during a coding interview?
Not just okay — expected. Clarifying assumptions (can values be negative? is the array sorted?) prevents you from solving the wrong problem and shows professional engineering instincts.
Summary
- String patterns: reversal, character frequency maps, regex for cleaning
- Array patterns: Set for uniqueness, reduce for aggregation, slice for sub-arrays
- Number patterns: modulo for divisibility, sqrt optimization for primality
- Object patterns: reduce for grouping, entries/fromEntries for transformation
- Algorithm patterns: binary search (O(log n)), hash map (O(1) lookup), stack for matching
- Use
constby default — switch toletonly when reassignment is needed - Prefer
Mapover plain objects when keys are dynamic - Handle edge cases explicitly: empty input, single element, all duplicates
- State the time and space complexity before or after writing your solution
- Test your solution with at least two examples including an edge case