HomeBytesheetsRoadmapsUI TemplatesUI ToolsStack Discussion
25 Topics
Chapter•180 Minutes

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.

Manual String Reversal
function reverseString(str) {
  let reversed = '';
  for (let i = str.length - 1; i >= 0; i--) {
    reversed += str[i];
  }
  return reversed;
}

console.log(reverseString('hello')); // 'olleh'
💡 By looping from str.length - 1 down to 0, each character is appended in reverse order without using Array.prototype.reverse().
Key Takeaways
  • 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.
Knowledge Check

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.

FizzBuzz Loop
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));
💡 Evaluating i % 15 first prevents multiples of both 3 and 5 from matching the earlier single-factor branches.
Key Takeaways
  • Check for combined conditions (i % 15 === 0) before individual conditions.
  • Demonstrates basic modulo arithmetic and control flow branching.
Knowledge Check

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.

Two-Pointer Palindrome Validation
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
💡 Two pointers move inward from opposite ends, allowing early exit as soon as a non-matching character pair is found.
Key Takeaways
  • Sanitizes strings using Regular Expressions prior to checking.
  • Uses a two-pointer approach to compare characters from opposite ends toward the center.
Knowledge Check

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.

Single-Pass Min/Max Finder
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 }
💡 Iterating once updates min and max tracking variables in O(n) time and O(1) auxiliary space.
Key Takeaways
  • Performs single-pass O(n) iteration to update bounds.
  • Avoids Math.max(...arr) which can throw a stack overflow error on very large arrays.
Knowledge Check

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().

Array Chunking Helper
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]]
💡 By incrementing the loop index by size each iteration, slice() extracts sub-arrays of exact length.
Key Takeaways
  • Advances loop pointer by size steps.
  • Array.prototype.slice extracts chunks cleanly without mutating the source array.
Knowledge Check

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.

Character Frequency Counter
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 }
💡 The expression (freq[char] || 0) ensures that unvisited characters start at 0 before adding 1.
Key Takeaways
  • Uses logical OR (||) to initialize missing object keys to zero.
  • Runs in linear O(n) time complexity relative to string length.
Knowledge Check

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.

Array Deduplication Approaches
// 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]
💡 ES6 Set automatically discards duplicate values in linear time, while filter + indexOf scans the array repeatedly.
Key Takeaways
  • Set approach provides O(n) time complexity.
  • filter + indexOf provides O(n²) time complexity due to internal lookup overhead.
Knowledge Check

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.

Object Key-Value Inversion
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' }
💡 Maps each value obj[key] as a key in the new object, assigning the original key string as its value.
Key Takeaways
  • Swaps keys and values into a new object.
  • Uses hasOwnProperty guard to prevent iterating inherited prototype properties.
Knowledge Check

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.

Object Pick and Omit Utility
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' }
💡 pick filters down to specified keys, while omit uses a Set lookup to drop listed keys efficiently.
Key Takeaways
  • Reduces object keys dynamically.
  • Using a Set in omit allows O(1) key exclusion checks.
Knowledge Check

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.

Infinitely Curried Sum
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
💡 Each invocation returns a new sum function wrapping the updated total until b === undefined triggers the final total.
Key Takeaways
  • Uses closure state to accumulate sum values across chained calls.
  • Terminates recursive currying when invoked without arguments (b === undefined).
Knowledge Check

What acts as the base termination signal for ending the sum currying chain above?