JavaScript Problems & Solutions
1. Write a program to remove duplicates from an array ? (Most Most Asked question)
const removeDuplicatesWay1 = (array) => {
let uniqueArr = [];
for (let i = 0; i <= array.length - 1; i++) {
if (uniqueArr.indexOf(array[i]) === -1) {
uniqueArr.push(array[i]);
}
}
return uniqueArr;
};
removeDuplicatesWay1([1, 2, 1, 3, 4, 2, 2, 1, 5, 8]);
// ---------------------- (or) ----------------------
function removeDuplicatesWay2(arr) {
// Use the set object to remove duplicates.
return Array.from(new Set(arr));
// return [...new Set(arr)] -> another way
}
removeDuplicatesWay2([1, 2, 1, 3, 4, 2, 2, 1, 5, 8]);
Detailed Explanation:
- Way 1 (Loop & IndexOf): We create an empty array called
uniqueArr. We loop through the input array and for every item, we check if it already exists in ouruniqueArr. IfindexOfreturns -1 (meaning it's not there), we add it. - Way 2 (Set Object): A
Setin JavaScript is a special collection that automatically ignores duplicate values. By passing our array intonew Set(arr), the duplicates are removed instantly. We then convert it back into an array usingArray.from()or the spread operator[...set].
2. Write a JavaScript function that takes an array of numbers and returns a new array with only the even numbers.
function findEvenNumbers(arr) {
const result = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i] % 2 === 0) {
result.push(arr[i]); // Add even numbers to the result
}
}
return result;
}
// Example usage:
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
console.log("Even numbers:", findEvenNumbers(numbers));
// Time complexity: O(N)
Detailed Explanation:
- The Goal: Create a new list containing only the numbers divisible by 2.
- The Modulo Operator (%): We use
num % 2 === 0to check if a number is even. This operator gives the remainder of a division; if the remainder is 0 when divided by 2, the number is even. - The Loop: We go through the array one by one, checking each number.
- Efficiency: This is O(N) because we only need to look at each number exactly once.
3. How to check whether a string is palindrome or not ?
const checkPalindrome = (str) => {
const len = str.length;
for (let i = 0; i < len / 2; i++) {
if (str[i] !== str[len - 1 - i]) {
return "Not palindrome";
}
}
return "palindrome";
};
console.log(checkPalindrome("madam"));
Detailed Explanation:
- The Goal: Check if a word reads the same forwards and backwards (like "madam").
- Efficiency: Instead of reversing the whole string, we only compare the first half with the second half.
- Comparison: We compare the character at index
iwith the character at the opposite end (len - 1 - i). - Early Exit: As soon as we find a pair of letters that don't match, we stop and say "Not palindrome". If the loop finishes, it means all pairs matched.
4. Find the factorial of given number ?
const findFactorial = (num) => {
if (num === 0 || num === 1) {
return 1;
} else {
return num * findFactorial(num - 1);
}
};
console.log(findFactorial(4));
Detailed Explanation:
- The Goal: Calculate the product of all positive integers up to
n(e.g., 4! = 4 * 3 * 2 * 1 = 24). - Recursion: This function calls itself. To find the factorial of 4, it says "It's 4 times the factorial of 3".
- Base Case: We must stop at 0 or 1, where the factorial is always 1. Without this, the function would run forever.
- Execution: The computer stacks up these multiplications until it hits the base case, then calculates the final total.
5. Program to find longest word in a given sentence ?
const findLongestWord = (sentence) => {
let wordsArray = sentence.split(" ");
let longestWord = "";
for (let i = 0; i < wordsArray.length; i++) {
if (wordsArray[i].length > longestWord.length) {
longestWord = wordsArray[i];
}
}
console.log(longestWord);
};
findLongestWord("Hi Iam Saikrishna Iam a UI Developer");
Detailed Explanation:
- The Goal: Extract the single word with the most characters from a sentence.
- Splitting: We use
split(" ")to turn the sentence into an array of individual words. - Comparison: We start with an empty string as our "current longest". As we loop through the words, if we find one that is longer than our current record, we update our record.
- Result: After looking at every word, the one stored in
longestWordis the winner.
6. Write a JavaScript program to find the maximum number in an array.
function findMax(arr) {
if (arr.length === 0) {
return undefined; // Handle empty array case
}
let max = arr[0]; // Initialize max with the first element
for (let i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i]; // Update max if current element is larger
}
}
return max;
}
// Example usage:
const numbers = [1, 6, -33, 8, 4, 0, 2];
console.log("Maximum number is:", findMax(numbers));
// Time complexity: O(N)
Detailed Explanation:
- The Goal: Find the largest value in a list of numbers.
- Initialization: We assume the first number (
arr[0]) is the largest to start with. - Iteration: We loop through the rest of the numbers. If we see a number bigger than our current
max, we update ourmaxto that new number. - Edge Case: We check if the array is empty at the very beginning to avoid errors.
- Efficiency: This is O(N) because we check each number exactly once.
7. Write a JavaScript function to check if a given number is prime.
function isPrime(number) {
if (number <= 1) {
return false; // 1 and numbers less than 1 are not prime
}
// Loop up to the square root of the number
for (let i = 2; i <= Math.sqrt(number); i++) {
if (number % i === 0) {
return false; // If divisible by any number, not prime
}
}
return true; // If not divisible by any number, it's prime
}
// Example usage:
console.log(isPrime(17)); // true
console.log(isPrime(18)); // false
// Time complexity: O(sqrt(N))
Detailed Explanation:
- The Goal: Determine if a number is "prime" (only divisible by 1 and itself).
- Base Case: Numbers 1 and below are not prime.
- The Loop (Optimization): We only need to check for divisors up to the square root of the number. Why? Because if a number has a divisor larger than its square root, it must also have one smaller than its square root.
- Efficiency: Checking up to the square root makes the function significantly faster for large numbers.
8. Program to find Reverse of a string without using built-in method ?
const findReverse = (sampleString) => {
let reverse = "";
for (let i = sampleString.length - 1; i >= 0; i--) {
reverse += sampleString[i];
}
console.log(reverse);
};
findReverse("Hello Iam Saikrishna UI Developer");
Detailed Explanation:
- The Goal: Flip a string backwards without using
.reverse(). - Manual Loop: We start a loop at the very last character of the string (
length - 1). - Building the Result: We iterate backwards toward the first character (index 0), adding each letter one by one to our
reversestring variable. - Result: Once the loop finishes, we have a complete backwards version of the original string.
9. Find the smallest word in a given sentence ?
function findSmallestWord() {
const sentence = "Find the smallest word";
const words = sentence.split(' ');
let smallestWord = words[0];
for (let i = 1; i < words.length; i++) {
if (words[i].length < smallestWord.length) {
smallestWord = words[i];
}
}
console.log(smallestWord);
}
findSmallestWord();
Detailed Explanation:
- The Goal: Find the word with the fewest characters in a sentence.
- Preparation: We use
split(' ')to break the sentence into an array of words. - Comparison: We assume the first word is the smallest. We then loop through the rest, comparing their lengths.
- Update: If we find a word with a smaller length, we update our
smallestWordvariable.
10. Write a function sumOfThirds(arr), which takes an array arr as an argument. This function should return a sum of every third number in the array, starting from the first one.
const sumOfThirds = (arr) => {
let sum = 0;
for (let i = 0; i < arr.length; i += 3) {
sum += arr[i];
}
return sum;
};
Detailed Explanation:
- The Goal: Sum specific items in an array—specifically every third one (index 0, 3, 6, etc.).
- The Step: In our
forloop, instead of usingi++, we usei += 3. This tells the computer to "jump" 3 spots forward after each addition. - Accumulation: We add the value at the current index to our
sumvariable until we reach the end of the array.
11. Write a JavaScript function that returns the Fibonacci sequence up to a given number of terms.
function fibonacciSequence(numTerms) {
if (numTerms <= 0) {
return [];
} else if (numTerms === 1) {
return [0];
}
const sequence = [0, 1];
for (let i = 2; i < numTerms; i++) {
const nextFibonacci = sequence[i - 1] + sequence[i - 2];
sequence.push(nextFibonacci);
}
return sequence;
}
// Example usage:
const numTerms = 10;
const fibonacciSeries = fibonacciSequence(numTerms);
console.log(fibonacciSeries); // Output: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
// Time complexity: O(N)
Detailed Explanation:
- The Goal: Generate a list of Fibonacci numbers (where each number is the sum of the two before it).
- Base Cases: If the user wants 0 or 1 terms, we handle those immediately.
- Seed Values: We start the list with
[0, 1]. - The Calculation: We start our loop at index 2. For each spot, we add the values from the previous two positions (
i-1andi-2) and push that new total into our list.
12. Find the max count of consecutive 1's in an array ?
const findConsecutive = (array) => {
let maxCount = 0;
let currentConsCount = 0;
for (let i = 0; i <= array.length - 1; i++) {
if (array[i] === 1) {
currentConsCount += 1;
maxCount = Math.max(currentConsCount, maxCount);
} else {
currentConsCount = 0;
}
}
console.log(maxCount);
};
findConsecutive([1, 1, 0, 1, 0, 1, 10, 7, 1, 1, 1, 3, 2, 5, 1, 1]);
// output: 3
Detailed Explanation:
- The Goal: Find the longest "streak" of the number 1 in a list.
- Counters: We use two variables:
currentConsCountto track the streak we are currently looking at, andmaxCountto remember the longest streak we've ever seen. - Resetting: If we encounter a 1, we increase our current streak. If we encounter anything else, the streak is broken, so we reset
currentConsCountto 0. - Updating the Record: Every time we increase the current streak, we check if it's bigger than our all-time record (
maxCount).
13. Given 2 arrays that are sorted [0,3,4,31] and [4,6,30]. Merge them and sort [0,3,4,4,6,30,31] ?
const sortedData = (arr1, arr2) => {
let i = 0;
let j = 0;
let array1 = arr1[0];
let array2 = arr2[0];
let mergedArray = [];
while (array1 !== undefined || array2 !== undefined) {
if (array2 === undefined || array1 < array2) {
mergedArray.push(array1);
array1 = arr1[i + 1];
i++;
} else {
mergedArray.push(array2);
array2 = arr2[j + 1];
j++;
}
}
console.log(mergedArray);
}
sortedData([1, 3, 4, 5], [2, 6, 8, 9]);
Detailed Explanation:
- The Goal: Take two lists that are already sorted and merge them into one single sorted list efficiently.
- Pointers: We use two "markers" (
iandj) to track where we are in each list. - Comparison: We look at the items at both markers. We take the smaller one, add it to our new list, and move that specific marker forward.
- Efficiency: Because the input lists are already sorted, we only need to walk through each list once. This is much faster than joining them and then sorting from scratch.
14. Create a function which will accepts two arrays arr1 and arr2. The function should return true if every value in arr1 has its corresponding value squared in array2. The frequency of values must be same. (Efficient)
function isSameFrequency(arr1, arr2) {
if (arr1.length !== arr2.length) {
return false;
}
let arrFreq1 = {};
let arrFreq2 = {};
for (let val of arr1) {
arrFreq1[val] = (arrFreq1[val] || 0) + 1;
}
for (let val of arr2) {
arrFreq2[val] = (arrFreq2[val] || 0) + 1;
}
for (let key in arrFreq1) {
if (!(key ** 2 in arrFreq2)) return false;
if (arrFreq1[key] !== arrFreq2[key ** 2]) return false;
}
return true;
}
console.log(isSameFrequency([1, 2, 5], [25, 4, 1]));
Detailed Explanation:
- The Goal: Check if the second array is a "squared" version of the first one, meaning every number in the first array has its square in the second array with the exact same frequency.
- Frequency Counters: Instead of nested loops (which are slow), we use two objects to count how many times each number appears in each array.
- Matching: We loop through our first frequency map. For each number, we check: "Is its square present in the second map?" and "Do the counts match?".
- Efficiency: This approach is much faster (O(N)) because we only pass through the data a few times instead of comparing every single pair.
15. Given two strings. Find if one string can be formed by rearranging the letters of other string. (Efficient)
function isStringCreated(str1, str2) {
if (str1.length !== str2.length) return false;
let freq = {};
for (let val of str1) {
freq[val] = (freq[val] || 0) + 1;
}
for (let val of str2) {
if (freq[val]) {
freq[val] -= 1;
} else {
return false;
}
}
return true;
}
console.log(isStringCreated('anagram', 'nagaram'));
Detailed Explanation:
- The Goal: Determine if two words are anagrams (contain the exact same letters in the same amounts).
- Frequency Mapping: We count the occurrences of each letter in the first string and store them in an object.
- Decremementing: We then loop through the second string. For every letter we find, we "subtract" one from our count in the object.
- Validation: If we ever find a letter that isn't in our object (or its count is already zero), we know the strings aren't anagrams.
- Efficiency: This is much faster than sorting the strings, especially for very long pieces of text.
16. Write logic to get unique objects from below array ?
function getUniqueArr(array) {
const uniqueArr = [];
const seen = {};
for (let i = 0; i < array.length; i++) {
const currentItem = array[i].name;
if (!seen[currentItem]) {
uniqueArr.push(array[i]);
seen[currentItem] = true;
}
}
return uniqueArr;
}
let arr = [
{ name: "sai" },
{ name: "Nang" },
{ name: "sai" },
{ name: "Nang" },
{ name: "111111" }
];
console.log(getUniqueArr(arr));
Detailed Explanation:
- The Goal: Remove duplicate objects from a list based on a specific property (in this case, the "name").
- The "Seen" Object: We use an object (
seen) to keep track of which names we have already encountered. - Filtering: As we loop through the array, we check our
seenobject. If the name isn't there, we add the whole object to our results and mark that name as "seen". - Result: Only the first occurrence of each unique name is kept in the final list.
17. Write a JavaScript program to find the largest element in a nested array.
function findLargestElement(arr) {
let max = Number.NEGATIVE_INFINITY;
function traverse(arr) {
for (let i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i])) {
traverse(arr[i]);
} else {
if (arr[i] > max) {
max = arr[i];
}
}
}
}
traverse(arr);
return max;
}
const nestedArray = [[3, 4, 58], [709, 8, 9, [10, 11]], [111, 2]];
console.log("Largest element:", findLargestElement(nestedArray));
Detailed Explanation:
- The Goal: Find the biggest number inside an array that might contain other arrays (boxes inside boxes).
- Recursion: We create a helper function called
traverse. If it sees another array, it calls itself to go "deeper". - Initialization: We start
maxat a very tiny number (NEGATIVE_INFINITY) so that any number we find will be bigger than it initially. - Update: Every time the function finds a plain number (not an array), it checks if that number is bigger than our current
max. If it is, we update our record.
18. Given a string, write a javascript function to count the occurrences of each character in the string.
function countCharacters(str) {
const charCount = {};
const len = str.length;
for (let i = 0; i < len; i++) {
const char = str[i];
charCount[char] = (charCount[char] || 0) + 1;
}
return charCount;
}
const result = countCharacters("hello");
console.log(result);
Detailed Explanation:
- The Goal: Create a summary that shows exactly how many times each letter appears in a word.
- Storage: We use an object where the letters will be "keys" and the counts will be "values".
- The Loop: We look at every single character from start to finish.
- Incrementing: For each character, we check our object. If we've seen it before, we add 1. If not, we start the count at 1.
- Output: The result is an object like
{ h: 1, e: 1, l: 2, o: 1 }.
19. Write a javascript function that sorts an array of numbers in ascending order.
function quickSort(arr) {
if (arr.length <= 1) {
return arr;
}
const pivot = arr[0];
const left = [];
const right = [];
for (let i = 1; i < arr.length; i++) {
if (arr[i] < pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return [...quickSort(left), pivot, ...quickSort(right)];
}
const unsortedArray = [5, 2, 9, 1, 5, 6];
const sortedArray = quickSort(unsortedArray);
console.log(sortedArray);
Detailed Explanation:
- The Goal: Sort a list of numbers from smallest to largest.
- Quick Sort Logic: We pick the first number as a "pivot".
- Partitioning: We create two piles:
leftfor numbers smaller than the pivot, andrightfor numbers larger. - Recursion: we do the same thing to the
leftandrightpiles until every number is in its correct place. - Efficiency: This is one of the fastest sorting algorithms, with a time complexity of O(n log n).
20. Write a javascript function that sorts an array of numbers in descending order.
function quickSort(arr) {
if (arr.length <= 1) {
return arr;
}
const pivot = arr[0];
const left = [];
const right = [];
for (let i = 1; i < arr.length; i++) {
if (arr[i] >= pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return [...quickSort(left), pivot, ...quickSort(right)];
}
const arr = [3, 1, 4, 1, 5, 9, 2, 6, 5];
console.log(quickSort(arr));
Detailed Explanation:
- The Goal: Sort numbers from largest to smallest.
- The Logic: We use the exact same Quick Sort algorithm as before, but with one tiny change.
- The Change: Instead of putting smaller numbers in the
leftpile, we put larger numbers (or equal ones) in theleftpile. - Result: Because the "larger" numbers always end up on the left side of the pivot, the final joined array will be sorted in descending order.
21. Write a JavaScript function that reverses the order of words in a sentence without using the built-in reverse() method.
const reverseWords = (sampleString) => {
let reversedSentence = "";
let word = "";
for (let i = 0; i < sampleString.length; i++) {
if (sampleString[i] !== " ") {
word += sampleString[i];
} else {
reversedSentence = word + " " + reversedSentence;
word = "";
}
}
reversedSentence = word + " " + reversedSentence;
console.log(reversedSentence.trim());
};
reverseWords("ChatGPT is awesome");
Detailed Explanation:
- The Goal: Take a sentence like "I love coding" and turn it into "coding love I" without using the built-in
.reverse()tool. - Character Loop: We look at every character. If it's a letter, we add it to our current
word. - Building Backwards: When we hit a space, it means the word is finished. We take that word and put it at the front of our results.
- Pre-pending: By always putting the new word before the old ones (
word + " " + reversedSentence), the sentence naturally builds itself backwards.
22. Implement a javascript function that flattens a nested array into a single-dimensional array.
function flattenArray(arr) {
const stack = [...arr];
const result = [];
while (stack.length) {
const next = stack.pop();
if (Array.isArray(next)) {
stack.push(...next);
} else {
result.push(next);
}
}
return result.reverse();
}
const nestedArray = [1, [2, [3, 4], [7, 5]], 6];
console.log(flattenArray(nestedArray));
Detailed Explanation:
- The Goal: Take an array that contains other arrays and turn it into one flat list.
- Iterative Approach: Instead of recursion, we use a "Stack" (a list of things to do).
- Popping and Checking: We take an item out of the stack. If it's another array, we unpack it and put its contents back into the stack.
- Collecting: If it's just a value, we add it to our
result. - Reversing: Because we are "popping" from the end of the stack, the results come out backwards, so we call
.reverse()at the very end to fix the order.
23. Write a function which converts string input into an object
function stringToObject(str, finalValue) {
const keys = str.split('.');
let result = {};
let current = result;
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
current[key] = (i === keys.length - 1) ? finalValue : {};
current = current[key];
}
return result;
}
const output = stringToObject("a.b.c", "someValue");
console.log(output); // { a: { b: { c: "someValue" } } }
Detailed Explanation:
- The Goal: Take a string like "user.name.first" and turn it into a deep object:
{ user: { name: { first: "value" } } }. - Splitting: We use
.split('.')to get an array of keys:["a", "b", "c"]. - Pointer Logic: we start with an empty
resultobject. We use a "pointer" (current) to keep track of where we are inside that object. - Creation: As we loop through the keys, we create a new empty object at the current key and move our pointer inside it.
- Final Step: For the very last key, instead of creating another empty object, we assign the
finalValue.
24. Given an array, return an array where the each value is the product of the next two items: E.g. [3, 4, 5] -> [20, 15, 12]
function productOfNextTwo(arr) {
const result = [];
for (let i = 0; i < arr.length; i++) {
if (i < arr.length - 2) {
result.push(arr[i + 1] * arr[i + 2]);
} else if (i === arr.length - 2) {
result.push(arr[i + 1] * arr[0]);
} else {
result.push(arr[0] * arr[1]);
}
}
return result;
}
const inputArray = [3, 4, 5];
const outputArray = productOfNextTwo(inputArray);
console.log(outputArray); // [20, 15, 12]
Detailed Explanation:
- The Goal: For every number in a list, multiply the next two numbers together to get a new value.
- Circular Logic: If there aren't two numbers left (at the end of the list), we "wrap around" to the beginning of the list.
- Conditions:
- Normal case: Multiply index
i+1andi+2. - Near end: Multiply index
i+1and the first number (index 0). - Last item: Multiply the first and second numbers (index 0 and 1).
- Normal case: Multiply index
25. Find the 2nd largest element from a given array ? [100, 20, 112, 22]
function findSecondLargest(arr) {
if (arr.length < 2) {
throw new Error("Array must contain at least two elements");
}
let largest = -Infinity;
let secondLargest = -Infinity;
for (let i = 0; i < arr.length; i++) {
if (arr[i] > largest) {
secondLargest = largest;
largest = arr[i];
} else if (arr[i] > secondLargest && arr[i] < largest) {
secondLargest = arr[i];
}
}
return secondLargest;
}
const array = [10, 5, 20, 8, 12];
console.log(findSecondLargest(array)); // 12
Detailed Explanation:
- The Goal: Find the "runner up" largest number in a single pass.
- Two Records: We keep track of the
largestand thesecondLargest. - New Winner: If we find a number bigger than our current
largest, the old champion becomes thesecondLargest, and the new number takes the top spot. - Middle Case: If a number isn't the new champion but is bigger than our current
secondLargest, it becomes the new runner-up.
26. Program challenge: Find the pairs from given input ?
function findPairs(input1, input2) {
const pairs = [];
const seen = new Set();
for (const num of input1) {
const complement = input2 - num;
if (seen.has(complement)) {
pairs.push([complement, num]);
}
seen.add(num);
}
return pairs;
}
const input1 = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const input2 = 10;
console.log(findPairs(input1, input2));
// [[1, 9], [2, 8], [3, 7], [4, 6], [5, 5]]
Detailed Explanation:
- The Goal: Find all pairs of numbers in a list that add up to a specific target (
input2). - The "Memory" Trick: We use a
Setto remember every number we've seen so far. - Calculation: For every number, we calculate its "complement" (
target - currentNumber). This is the number we need to complete the pair. - Checking: If the complement is already in our
seenlist, we've found a pair! - Efficiency: This is extremely fast (O(N)) because we only go through the list once.
27. Write a javascript program to get below output from given input ?
function encodeString(input) {
if (input.length === 0) return "";
let result = "";
let count = 1;
for (let i = 1; i < input.length; i++) {
if (input[i] === input[i - 1]) {
count++;
} else {
result += count + input[i - 1];
count = 1;
}
}
result += count + input[input.length - 1];
return result;
}
const input = "abbcccddddeea";
console.log(encodeString(input)); // "1a2b3c4d2e1a"
Detailed Explanation:
- The Goal: Compress a string by replacing repeating letters with their counts (e.g., "aaa" becomes "3a").
- Counting Streaks: We loop through the string, comparing each letter to the one before it.
- Same Letter: If they are the same, we increase our
count. - New Letter: If they are different, we "write down" the count and the letter, then reset the count to 1 for the new letter.
- Final Touch: We have to add the very last streak after the loop finishes.
28. Implement a memoization function.
Memoization is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls.
function memoize(fn) {
const cache = {};
return function(...args) {
const key = JSON.stringify(args);
if (cache[key]) {
return cache[key];
}
const result = fn.apply(this, args);
cache[key] = result;
return result;
};
}
// Usage:
const expensiveFunc = (num) => {
console.log("Computing...");
return num * 2;
};
const memoizedFunc = memoize(expensiveFunc);
console.log(memoizedFunc(5)); // Computing... 10
console.log(memoizedFunc(5)); // 10 (from cache)
Detailed Explanation:
- The Goal: Make slow functions faster by "remembering" previous results.
- The Cache: We create a simple object to store results.
- The Key: We turn the function's arguments into a string to use as a unique ID for that specific call.
- Check First: Before doing any work, we check if the result is already in our cache. If it is, we return it instantly.
- Store Result: If it's a new call, we run the function, save the result in the cache, and then return it.
29. Create a polyfill for the `map` method.
The map() method creates a new array populated with the results of calling a provided function on every element in the calling array.
Array.prototype.myMap = function(callback) {
const result = [];
for (let i = 0; i < this.length; i++) {
result.push(callback(this[i], i, this));
}
return result;
};
// Usage:
const doubled = [1, 2, 3].myMap(num => num * 2);
console.log(doubled); // [2, 4, 6]
Detailed Explanation:
- The Goal: Transform every item in an array into something else using a rule (callback).
- Empty Container: We start with a fresh, empty array to store our new values.
- Looping: We use a standard
forloop to visit every item in the original array (this). - Transformation: For each item, we run the callback function and take whatever it "returns" and put it in our new array.
- Return: Finally, we return the new transformed list, leaving the original array untouched.
30. Create a polyfill for the `filter` method.
The filter() method creates a shallow copy of a portion of a given array, filtered down to just the elements from the given array that pass the test.
Array.prototype.myFilter = function(callback) {
const result = [];
for (let i = 0; i < this.length; i++) {
if (callback(this[i], i, this)) {
result.push(this[i]);
}
}
return result;
};
// Usage:
const evens = [1, 2, 3, 4].myFilter(num => num % 2 === 0);
console.log(evens); // [2, 4]
Detailed Explanation:
- The Goal: Pick out only certain items from an array based on a "pass/fail" test.
- The Test: The callback function must return
truefor items we want to keep andfalsefor items we want to discard. - Looping: We look at every item in the list.
- Decision: If the callback says
truefor an item, we "push" it into our new list. - Result: We end up with a smaller (or equal sized) array containing only the items that passed the test.
31. Implement a function to flatten a nested array.
This function converts a multi-dimensional array into a single-dimensional one.
function flatten(arr) {
return arr.reduce((acc, val) => {
return Array.isArray(val) ? acc.concat(flatten(val)) : acc.concat(val);
}, []);
}
// Usage:
const nested = [1, [2, [3, 4], 5], 6];
console.log(flatten(nested)); // [1, 2, 3, 4, 5, 6]
Detailed Explanation:
- The Goal: We want to take an array that contains other arrays (like boxes inside boxes) and unpack everything into one flat list.
- Reduce: We use
reduceto go through the array and build up a single "accumulator" list. - Recursion: For every item, we check: "Is this item an array?".
- Unpacking: If it is an array, we call
flattenon it again (this is recursion) to unpack its contents. If it's just a value, we simply add it to our list. - Merging: We use
concatto join these unpacked items into our main list.
32. Create a polyfill for the `bind` method.
The bind() method creates a new function that, when called, has its this keyword set to the provided value.
Function.prototype.myBind = function(context, ...args) {
const fn = this;
return function(...newArgs) {
return fn.apply(context, [...args, ...newArgs]);
};
};
// Usage:
const user = { name: "John" };
function greet(greeting) { console.log(`${greeting}, ${this.name}`); }
const boundGreet = greet.myBind(user, "Hello");
boundGreet(); // Hello, John
Detailed Explanation:
- The Goal: We want to "lock" a specific object to a function so that whenever the function is called,
thisalways refers to that object. - Returning a Function:
binddoesn't run the function immediately; it returns a new one that you can use later. - The Context: We save the original function (
this) and the object you want to use (context). - Combining Arguments: We allow the user to pass some arguments during binding (
args) and some during the actual call (newArgs). We merge these together using the spread operator. - Execution: When the new function is finally called, it uses
applyto run the original function with the correct context and all merged arguments.
33. Create a polyfill for the `apply` method.
The apply() method calls a function with a given this value, and arguments provided as an array.
Function.prototype.myApply = function(context, argsArray) {
context = context || window;
const uniqueId = Symbol();
context[uniqueId] = this;
const result = context[uniqueId](...argsArray);
delete context[uniqueId];
return result;
};
Detailed Explanation:
- The Goal: Run a function immediately while manually setting what
thisshould be, passing arguments as a single array. - Temporary Property: To make the function "belong" to the context object, we temporarily attach it to that object using a unique key (
Symbol). - Running the Function: We call the function from that object:
context[uniqueId](...argsArray). This naturally setsthisto the object. - Cleaning Up: After we get the result, we delete the temporary property from the object so it stays clean.
- Spread Operator: We use
...to turn the array of arguments back into individual items for the function call.
34. Create a polyfill for the `call` method.
The call() method calls a function with a given this value and arguments provided individually.
Function.prototype.myCall = function(context, ...args) {
context = context || window;
const uniqueId = Symbol();
context[uniqueId] = this;
const result = context[uniqueId](...args);
delete context[uniqueId];
return result;
};
Detailed Explanation:
- The Goal: Similar to
apply, but instead of an array, arguments are passed one by one. - The Logic: We use the same trick as
myApply: attach the function to the object temporarily so thatthispoints to it. - Arguments: We use rest parameters (
...args) to collect all arguments passed intomyCall). - Execution: We run the function using the object context and then remove the temporary reference.
35. Create a polyfill for `Promise.all`.
Promise.all() takes an iterable of promises and returns a single Promise that resolves when all of the promises have resolved.
function myPromiseAll(promises) {
return new Promise((resolve, reject) => {
const results = [];
let completedCount = 0;
promises.forEach((p, index) => {
Promise.resolve(p).then((val) => {
results[index] = val;
completedCount++;
if (completedCount === promises.length) {
resolve(results);
}
}).catch(reject);
});
});
}
Detailed Explanation:
- The Goal: We want to wait for many tasks to finish. If they all succeed, give us all the results. If even one fails, fail the whole thing.
- The Wrapper: We return one big "Super Promise".
- Tracking: We keep an array (
results) to store the output of each promise and a counter (completedCount) to know how many have finished. - Order Matters: We store the results using the
indexfrom the loop. This ensures the output array matches the order of the input array, even if the promises finish at different times. - Failure: If any promise fails, we immediately call
reject, which stops everything.
36. Write a function to check if two strings are anagrams.
Two strings are anagrams if they contain the same characters in the same frequency.
function isAnagram(str1, str2) {
if (str1.length !== str2.length) return false;
const s1 = str1.split('').sort().join('');
const s2 = str2.split('').sort().join('');
return s1 === s2;
}
console.log(isAnagram("listen", "silent")); // true
Detailed Explanation:
- The Goal: Check if two words are made up of the exact same letters (like "listen" and "silent").
- Quick Check: If the lengths are different, they definitely aren't anagrams, so we return
falseimmediately. - Standardizing: We turn both strings into lowercase (optional but recommended), split them into individual characters, and sort them alphabetically.
- Comparison: If both strings have the same letters, they will look identical once sorted. We join them back into strings and compare them.
37. Implement the Fibonacci series using recursion and iteration.
Recursive:
function fibRecursive(n) {
if (n <= 1) return n;
return fibRecursive(n - 1) + fibRecursive(n - 2);
}
Iterative:
function fibIterative(n) {
const series = [0, 1];
for (let i = 2; i <= n; i++) {
series.push(series[i - 1] + series[i - 2]);
}
return series[n];
}
Detailed Explanation:
- The Goal: Calculate numbers where each number is the sum of the two preceding ones (0, 1, 1, 2, 3, 5, 8...).
- Recursion: This is the "elegant" way. A number is just the sum of the previous two numbers. However, this is slow because it calculates the same values over and over again.
- Iteration: This is the "efficient" way. We start with
[0, 1]and use a loop to keep adding the last two numbers until we reach the target. - Trade-off: Iteration is much faster for large numbers because it only does the work once per number.
38. Create a polyfill for the `debounce` function.
Debouncing ensures that a function is only called after a certain amount of time has passed since the last time it was invoked.
function debounce(fn, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
Detailed Explanation:
- The Goal: Stop a function from running too often. For example, if a user is typing in a search box, we only want to search once they stop typing for a moment.
- The Timer: Every time the user triggers the event, we "cancel" any existing timer and start a brand new one.
- Wait Time: If the user triggers it again before the timer finishes, the old timer is cleared, and they have to wait the full
delayagain. - Execution: The function only runs if the timer finally reaches zero without being interrupted.
39. Create a polyfill for the `throttle` function.
Throttling ensures that a function is called at most once in a specified time interval.
function throttle(fn, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
fn.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
Detailed Explanation:
- The Goal: Limit how often a function can run. For example, if a user is scrolling, we might only want to run a calculation every 100ms, even if they scroll 1000 times.
- The Gatekeeper: We use a variable
inThrottleto act like a gate. - Locked State: When the function runs, we "lock" the gate (
inThrottle = true). - Cool Down: We set a timer to "unlock" the gate after the
limittime has passed. - Ignoring Calls: Any calls made while the gate is locked are simply ignored.
40. Find the number of occurrences of each character in a string.
function countChars(str) {
const counts = {};
for (let char of str) {
counts[char] = (counts[char] || 0) + 1;
}
return counts;
}
console.log(countChars("hello")); // {h: 1, e: 1, l: 2, o: 1}
Detailed Explanation:
- The Goal: Count how many times every letter appears in a piece of text.
- The Storage: We use an object where the letters are keys and the numbers are values.
- The Loop: We look at every character in the string one by one.
- Incrementing: For each character, we check: "Have we seen this before?". If yes, we add 1. If no, we start at 0 and add 1.
- Result: We get back an object that summarizes the letter counts.
41. Check if a string is a palindrome.
A palindrome reads the same forwards and backwards.
function isPalindrome(str) {
const reversed = str.split('').reverse().join('');
return str === reversed;
}
console.log(isPalindrome("racecar")); // true
Detailed Explanation:
- The Goal: Determine if a word is the same when flipped (like "mom" or "racecar").
- Reversing: We take the string, split it into letters, flip the list, and join it back together.
- Comparison: We simply check if the original string is exactly equal to the reversed version.
- Tip: In a real-world scenario, you should remove spaces and punctuation and convert to lowercase before checking.
42. Write a function to reverse a string.
function reverseString(str) {
return str.split('').reverse().join('');
}
Detailed Explanation:
- The Goal: Flip a string backwards.
- Step 1:
split('')turns the string into an array of individual letters. - Step 2:
reverse()is a built-in array method that flips the order. - Step 3:
join('')puts the letters back into a single string.
43. Solve the "Two Sum" problem.
Find two numbers in an array that add up to a specific target and return their indices.
function twoSum(nums, target) {
const map = new Map();
for (let i = 0; i < nums.length; i++) {
const complement = target - nums[i];
if (map.has(complement)) {
return [map.get(complement), i];
}
map.set(nums[i], i);
}
return [];
}
Detailed Explanation:
- The Goal: Given a list of numbers and a target sum, find the positions of the two numbers that add up to that target.
- The "Memory" Trick: Instead of checking every pair (which is slow), we remember every number we've seen so far.
- Calculating the Complement: For every number, we ask: "What other number do I need to reach the target?". This is
target - currentNumber. - Checking Memory: We look in our
Mapto see if we've already encountered that needed number. - Success: If we find it, we return the index of the old number and the index of the current one.
44. Implement a custom Event Emitter.
A simple implementation of the observer pattern.
class EventEmitter {
constructor() {
this.events = {};
}
on(event, listener) {
if (!this.events[event]) this.events[event] = [];
this.events[event].push(listener);
}
emit(event, ...args) {
if (this.events[event]) {
this.events[event].forEach(cb => cb(...args));
}
}
}
Detailed Explanation:
- The Goal: Create a system where one part of your code can "shout" (emit) an event and other parts can "listen" for it.
- Storage: We use an object to store lists of callback functions for each event name.
- Subscribing (on): When you call
on, you are telling the system: "When 'click' happens, run this function". We add that function to our list. - Broadcasting (emit): When you call
emit, you are saying: "The 'click' event just happened!". We go through our list for that event and run every single function we saved.
45. Find the first non-repeating character in a string.
function firstNonRepeating(str) {
const map = {};
for (let char of str) map[char] = (map[char] || 0) + 1;
for (let char of str) {
if (map[char] === 1) return char;
}
return null;
}
Detailed Explanation:
- The Goal: Find the very first letter in a word that only appears once.
- Counting: First, we loop through the string to count how many times every letter appears (using an object).
- Finding: We loop through the string a second time. This time, we look at each letter and check our counts.
- Priority: Because we loop in order from start to finish, the first letter we find with a count of 1 is our answer.
- No Match: If we finish both loops and find nothing, we return
null.
46. Find the longest substring without repeating characters.
function longestUniqueSubstr(str) {
let maxLen = 0;
let start = 0;
const seen = new Map();
for (let end = 0; end < str.length; end++) {
if (seen.has(str[end])) {
start = Math.max(start, seen.get(str[end]) + 1);
}
seen.set(str[end], end);
maxLen = Math.max(maxLen, end - start + 1);
}
return maxLen;
}
Detailed Explanation:
- The Goal: Find the length of the longest part of a word where no letter is repeated.
- Sliding Window: Imagine a "window" that slides across the word.
startis the left side andendis the right side. - Remembering: We use a
Mapto remember where we last saw each letter. - Encountering a Duplicate: If we see a letter we've seen before, we "shrink" the window from the left (
start) to move past the old instance of that letter. - Updating Max: As we slide the right side (
end) forward, we keep track of the biggest the window has ever been.
47. Implement a Stack using an array.
A LIFO (Last-In-First-Out) data structure.
class Stack {
constructor() { this.items = []; }
push(val) { this.items.push(val); }
pop() { return this.items.pop(); }
peek() { return this.items[this.items.length - 1]; }
isEmpty() { return this.items.length === 0; }
}
Detailed Explanation:
- The Goal: Create a data structure like a "stack of plates". You can only add to the top and take from the top.
- LIFO: This stands for "Last-In, First-Out". The last item you put in is the first one you get out.
- Methods:
push: Adds an item to the top.pop: Removes and returns the top item.peek: Just looks at the top item without removing it.
- Implementation: We use a simple JavaScript array.
pushandpopon an array naturally happen at the "end" (top) of the list.
48. Implement a Queue using an array.
A FIFO (First-In-First-Out) data structure.
class Queue {
constructor() { this.items = []; }
enqueue(val) { this.items.push(val); }
dequeue() { return this.items.shift(); }
isEmpty() { return this.items.length === 0; }
}
Detailed Explanation:
- The Goal: Create a data structure like a "line at a store". The first person to join the line is the first one to be served.
- FIFO: This stands for "First-In, First-Out".
- Methods:
enqueue: Add someone to the back of the line (push).dequeue: Remove someone from the front of the line (shift).
- Efficiency Note: In a real-world high-performance app, using
shift()can be slow for very large lists because it has to re-index the whole array. For small lists, it works perfectly.
49. Implement Binary Search.
A fast search algorithm for sorted arrays.
function binarySearch(arr, target) {
let left = 0, 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;
}
Detailed Explanation:
- The Goal: Find a specific number in a sorted list very quickly.
- Divide and Conquer: Instead of checking every number, we look at the middle.
- Elimination: If the middle number is too small, we know our target must be on the right side. If it's too big, it must be on the left.
- Halving: We cut the list in half every single time. This is incredibly fast (logarithmic time).
- Requirements: This ONLY works if the array is already sorted.
50. Implement Quick Sort.
A divide-and-conquer sorting algorithm.
function quickSort(arr) {
if (arr.length <= 1) return arr;
const pivot = arr[0];
const left = [], right = [];
for (let i = 1; i < arr.length; i++) {
arr[i] < pivot ? left.push(arr[i]) : right.push(arr[i]);
}
return [...quickSort(left), pivot, ...quickSort(right)];
}
Detailed Explanation:
- The Goal: Sort an array efficiently.
- The Pivot: Pick one number as a "pivot" (reference point).
- Partitioning: Move all numbers smaller than the pivot to a
leftlist and all numbers larger to arightlist. - Recursion: Do the exact same thing to the
leftlist and therightlist until they are all sorted. - Combining: Put the sorted left, the pivot, and the sorted right back together.
51. Implement Merge Sort.
function mergeSort(arr) {
if (arr.length <= 1) return arr;
const mid = Math.floor(arr.length / 2);
const left = mergeSort(arr.slice(0, mid));
const right = mergeSort(arr.slice(mid));
return merge(left, right);
}
function merge(left, right) {
let result = [], l = 0, r = 0;
while (l < left.length && r < right.length) {
if (left[l] < right[r]) result.push(left[l++]);
else result.push(right[r++]);
}
return result.concat(left.slice(l)).concat(right.slice(r));
}
Detailed Explanation:
- The Goal: Sort an array by breaking it down into tiny pieces and then carefully zipping them back together in order.
- Splitting: We keep cutting the array in half until we have many arrays with only one item each (which are technically "sorted").
- Merging: We then "merge" these small arrays back together.
- Comparing: When merging two arrays, we look at the first item of each and pick the smaller one to put into our result.
- Stable Sort: This is a very reliable sorting algorithm that performs consistently well.
52. Implement a function to deep clone an object.
function deepClone(obj) {
if (obj === null || typeof obj !== "object") return obj;
const clone = Array.isArray(obj) ? [] : {};
for (let key in obj) {
clone[key] = deepClone(obj[key]);
}
return clone;
}
Detailed Explanation:
- The Goal: Create a perfect copy of an object. Unlike a "shallow" copy, if the object has other objects inside it, we want copies of those too, not just references.
- Recursion: We look at every property. If the property is itself an object, we call
deepCloneon it. - Base Case: If the value is just a string, number, or null, we just return it as is.
- Array vs Object: We check if we are cloning an array (
[]) or a standard object ({}) so we use the right starting container.
53. Find the missing number in an array from 1 to N.
function findMissing(arr, n) {
const expectedSum = (n * (n + 1)) / 2;
const actualSum = arr.reduce((a, b) => a + b, 0);
return expectedSum - actualSum;
}
Detailed Explanation:
- The Goal: You have a list of numbers from 1 to N, but one is missing. Find it.
- The Math Secret: There is a famous math formula to calculate the sum of numbers from 1 to N:
(n * (n + 1)) / 2. - Calculating Expected: We use that formula to find out what the total should be.
- Calculating Actual: We add up all the numbers actually in our list.
- The Difference: The difference between what we expected and what we have is the missing number.
54. Program to find the factorial of a number.
function factorial(n) {
if (n === 0) return 1;
return n * factorial(n - 1);
}
Detailed Explanation:
- The Goal: Multiply a number by every number below it (e.g., 5! = 5 * 4 * 3 * 2 * 1 = 120).
- Recursion: We say: "The factorial of 5 is just 5 times the factorial of 4".
- Base Case: We have to stop somewhere! Factorial of 0 is defined as 1.
- Calculation: The function keeps calling itself with smaller and smaller numbers until it hits 0, then it multiplies all the results back up.