25 Progressive JavaScript Coding Questions (Easy to Difficult)
A 25-question coding bank structured from basic JavaScript string/array manipulations to advanced asynchronous control flow, custom polyfills, and engine metaprogramming.
Q51: Reverse a String Without Built-In Reverse
To reverse a string manually, iterate backward from the last character to the first, appending each character to a new string. This demonstrates basic iteration and string accumulation.
function reverseString(str) {
let reversed = '';
for (let i = str.length - 1; i >= 0; i--) {
reversed += str[i];
}
return reversed;
}
console.log(reverseString('hello')); // 'olleh'- Iterates backward from the end of the string to assemble a new reversed string.
- Runs with O(n) time complexity and O(n) space complexity.
What is the time complexity of reversing a string of length n character-by-character?
Q52: Classic FizzBuzz Implementation
Output numbers from 1 to N, replacing multiples of 3 with 'Fizz', 5 with 'Buzz', and multiples of both with 'FizzBuzz'. Check compound conditions (multiples of 15) before individual factors.
function fizzBuzz(n) {
const result = [];
for (let i = 1; i <= n; i++) {
if (i % 15 === 0) result.push('FizzBuzz');
else if (i % 3 === 0) result.push('Fizz');
else if (i % 5 === 0) result.push('Buzz');
else result.push(i.toString());
}
return result;
}
console.log(fizzBuzz(15));- Check for combined conditions (i % 15 === 0) before individual conditions.
- Demonstrates basic modulo arithmetic and control flow branching.
Why must the condition for 15 (or both 3 and 5) be checked first in FizzBuzz?
Q53: Check if a String is a Palindrome
Clean non-alphanumeric characters and convert to lower case, then use a two-pointer approach moving toward the center to check if the string reads identically forward and backward.
function isPalindrome(str) {
const cleaned = str.toLowerCase().replace(/[^a-z0-9]/g, '');
let left = 0;
let right = cleaned.length - 1;
while (left < right) {
if (cleaned[left] !== cleaned[right]) return false;
left++;
right--;
}
return true;
}
console.log(isPalindrome('A man, a plan, a canal: Panama')); // true- Sanitizes strings using Regular Expressions prior to checking.
- Uses a two-pointer approach to compare characters from opposite ends toward the center.
What is the primary benefit of a two-pointer approach over str === str.split('').reverse().join('')?
Q54: Find Minimum and Maximum Values in an Array
Find both min and max values in a single pass across the array without relying on spread syntax with Math.min/Math.max, which can overflow the call stack on large arrays.
function findMinMax(arr) {
if (!arr.length) return { min: undefined, max: undefined };
let min = arr[0];
let max = arr[0];
for (let i = 1; i < arr.length; i++) {
if (arr[i] < min) min = arr[i];
if (arr[i] > max) max = arr[i];
}
return { min, max };
}
console.log(findMinMax([3, 1, 9, 4, 2])); // { min: 1, max: 9 }- Performs single-pass O(n) iteration to update bounds.
- Avoids Math.max(...arr) which can throw a stack overflow error on very large arrays.
Why can Math.max(...largeArray) fail on extremely large arrays (e.g. 100,000+ elements)?
Q55: Chunk an Array into Sub-Arrays of Fixed Size
Split an array into smaller sub-arrays of a target size by iterating through the source array in increments of the chunk size and calling Array.prototype.slice().
function chunkArray(arr, size) {
const chunked = [];
for (let i = 0; i < arr.length; i += size) {
chunked.push(arr.slice(i, i + size));
}
return chunked;
}
console.log(chunkArray([1, 2, 3, 4, 5], 2)); // [[1, 2], [3, 4], [5]]- Advances loop pointer by size steps.
- Array.prototype.slice extracts chunks cleanly without mutating the source array.
What is the result of chunkArray([1, 2, 3, 4, 5], 2)?
Q56: Count Character Frequencies in a String
Count the occurrence of each character in a string by accumulating counts inside a plain object map, initializing unseen characters to zero.
function charFrequency(str) {
const freq = {};
for (let char of str) {
freq[char] = (freq[char] || 0) + 1;
}
return freq;
}
console.log(charFrequency('hello')); // { h: 1, e: 1, l: 2, o: 1 }- Uses logical OR (||) to initialize missing object keys to zero.
- Runs in linear O(n) time complexity relative to string length.
What value does freq[char] || 0 evaluate to if char is not yet present in the freq map?
Q57: Remove Duplicate Values from an Array
Deduplicate elements in an array using an ES6 Set for linear performance, or Array.prototype.filter() with indexOf() for a single-line functional approach.
// Approach 1: ES6 Set (Optimal O(n))
const uniqueSet = (arr) => [...new Set(arr)];
// Approach 2: Array filter (O(n²))
const uniqueFilter = (arr) => arr.filter((item, index) => arr.indexOf(item) === index);
console.log(uniqueSet([1, 2, 2, 3, 4, 4])); // [1, 2, 3, 4]- Set approach provides O(n) time complexity.
- filter + indexOf provides O(n²) time complexity due to internal lookup overhead.
Why is the Set approach preferred over filter + indexOf for large arrays?
Q58: Invert Keys and Values of an Object
Swap the keys and values of a plain object into a new object using a for...in loop combined with a hasOwnProperty check to ignore prototype properties.
function invertObject(obj) {
const inverted = {};
for (let key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
inverted[obj[key]] = key;
}
}
return inverted;
}
console.log(invertObject({ a: 'x', b: 'y' })); // { x: 'a', y: 'b' }- Swaps keys and values into a new object.
- Uses hasOwnProperty guard to prevent iterating inherited prototype properties.
What happens if an object contains multiple duplicate values during inversion (e.g. { a: 1, b: 1 })?
Q59: Implement Custom pick and omit Object Helpers
Build pick(obj, keys) to extract specific properties into a new object, and omit(obj, keys) to create a copy that excludes specified keys.
function pick(obj, keys) {
return keys.reduce((acc, key) => {
if (key in obj) acc[key] = obj[key];
return acc;
}, {});
}
function omit(obj, keys) {
const keySet = new Set(keys);
return Object.keys(obj).reduce((acc, key) => {
if (!keySet.has(key)) acc[key] = obj[key];
return acc;
}, {});
}
const user = { id: 1, name: 'Alex', role: 'admin' };
console.log(pick(user, ['name', 'role'])); // { name: 'Alex', role: 'admin' }
console.log(omit(user, ['role'])); // { id: 1, name: 'Alex' }- Reduces object keys dynamically.
- Using a Set in omit allows O(1) key exclusion checks.
What is the benefit of passing keys as a Set inside the omit function?
Q60: Implement Infinite Curried Addition sum(1)(2)(3)...()
Create a function that continuously accumulates numbers across chained function calls until called with no arguments to signal termination.
function sum(a) {
return function(b) {
if (b !== undefined) {
return sum(a + b);
}
return a;
};
}
console.log(sum(1)(2)(3)()); // 6
console.log(sum(5)(10)()); // 15- Uses closure state to accumulate sum values across chained calls.
- Terminates recursive currying when invoked without arguments (b === undefined).
What acts as the base termination signal for ending the sum currying chain above?