10 Must Know Coding Problems for Frontend Developers
Introduction
Coding problems are a definite part of every interview. In this article, I'm going to share the top 10 coding problems, their solutions, and the concepts behind them.
Problem - 1
Implement a cache-based solution for a function being called frequently. For example, if someone has written a function sum
to add two numbers and he/she calls the functions like `sum(2,4) then that function calculates the sum 2 + 4 and returns 6 as the result, correct?
if the same function sum
is called once again with the same arguments 2 and 4 as sum(2,4)
then this time the function should not calculate the result but return the result directly as it had calculated the result in the previous call.
The whole idea here is that we should not do unnecessary calculations. If sum(2,4)
was calculated earlier then this result should be utilized wherever it fits.
Now let's write the code.
function cachFunction(func){
const resultsCache = {};
return function (a,b){
const key = JSON.stringify(`${a},${b}`);
if(resultsCache[key]) {
return resultsCache[key];
}
else{
const res = func(a,b);
resultsCache[key] = res;
return res;
}
}
}
const sum = (a,b) => {
console.log("sum is called with", a, b);
return a + b;
}
const cachedSum = cachFunction(sum)
console.log(cachedSum(2,4));
console.log(cachedSum(8,4));
console.log(cachedSum(7,4));
console.log(cachedSum(8,4));
console.log(cachedSum(2,4));
Spend some time understanding the code.
Problem - 2
Problem Statement - Write function sum such that sum(1)(3)(4)....sum(n)() --> returns 1+2+3+.....+n. For Example - sum(1)(2)(3)(4)() should return 10 as 1+2+3+4 = 10
The idea behind this problem is to check your understanding of Currying in JavaScript.
What is currying?
Function currying is transforming a JavaScript function from callable as sum(a,b) to callable as sum(a)(b) and producing the exact same result. Look at a simple example code below.
// A simple example --> transforming sum(a,b) --> sum(a)(b)
const sum1 = (a,b) => a+b;
const addition1 = sum1(3, 4);
console.log(addition1); // 7
// transforming the sum in curryied sum
function sum2(a){
return function(b) {
return a + b;
}
}
const addition2 = sum2(3)(4);
console.log(addition2); // 7
Simple, right?
Let's write a solution to the Problem - 2
function sum(a){
return function curried(b){
if(!b){
return a;
}
return sum(a+b)
}
}
console.log(sum(1)(2)(3)(4)()); // 10
Problem - 3
Write a compose function that takes any number of functions as arguments, applies those functions in a sequence, and returns a final result.
// Suppose we have three functions as shown below ;
const sum = (x) => x + 5;
const multiply = (x) => x*4;
const subtract = (x) => x - 5;
// Simple Way
//Let us apply these 3 and get a final result;
const res1 = sum(5); // 10 returned
const res2 = multiply(res1); // 40
const finalResult = subtract(res2); // 35
console.log(finalResult); // 35
// The Compose Way ๐
const compose = (...fns) => (value) => fns.reduceRight((acc,fn) => fn(acc), value)
// it goes right to left, firstly sum is invoked then multiple and finally subtract
console.log(compose(subtract, multiply, sum)(5))
Problem - 4
Write the pipe function. This works the same as the compose function except it applies the functions from left to right, one by one.
const pipe = (...fns) => (value) => fns.reduce((acc,fn) => fn(acc), value)
// it goes left to right, firstly sum is invoked then multiple, and finally subtract.
console.log(pipe(sum, multiply, subtract)(5)) // 35
Problem - 5
Deep flatten a nested object. The object will be infinitely nested. You have to make that flat and remove the nesting. See the result of the code written below.
// Deep flatten a nested object
function deepFlatten(obj) {
const res = {};
function flat(ob, parent = ""){
for(let key in ob){
if(typeof ob[key] == 'object'){
flat(ob[key], parent + key + ".")
}
else{
res[parent + key] = ob[key]
}
}
}
flat(obj, "");
return res;
};
const obj = {
A: "12",
B: 23,
C: {
P: 23,
O: {
L: 56
},
Q: [1, 2]
}
};
const result = deepFlatten(obj);
console.log(result); // { A: '12', B: 23, 'C.P': 23, 'C.O.L': 56, 'C.Q.0': 1, 'C.Q.1': 2 }
Problem - 6
Deep flatten an infinitely nested array without using any built-in function.
input --> [[[1, [1.1]], 2, 3], [4, 5]];
output --> [1, 1.1, 2, 3, 4, 5];
disclaimer: This might not be the best solution. Feel free to propose a new solution if find anything wrong here.
*/
function flattenArray(array){
const res = [];
function flattenArr(arr){
for(let ele of arr){
//Check if the ele is an array
if(Array.isArray(ele)){
flattenArr(ele)
}
else{
res.push(ele)
}
}
}
flattenArr(array);
return res;
}
const arr = [[[1, [1.1]], 2, 3], [4, 5]]
console.log(flattenArray(arr));
Problem - 7
Write polyfill for Array.prototype.filter
As you know, there is a built-in method filter
in JavaScript. We have to write our own filter method that behaves exactly the same as the built-in one.
Array.prototype.filter = function (callback) {
//new array to store the filtered elements
const result = [];
for (let i = 0; i < this.length; i++) {
//call the callback with the current element, index, and context.
//if it passes the test then add the element in the new array.
if (callback(this[i], i, this)) {
result.push(this[i]);
}
}
//return the array
return result
}
If you want to learn about map, filter, and reduce methods in detail, check this article.
Problem - 8
Write the polyfill for Promise.all()
method.
As per MDN โ The Promise.all() accepts an array of promises and returns a promise that resolves when all of the promises in the array are fulfilled or when the iterable contains no promises. It rejects with the reason of the first promise that rejects.
function promiseAll(asyncTasksArray) {
//to store results
const results = [];
//to track how many promises have completed
let promisesCompleted = 0;
// return new promise
return new Promise((resolve, reject) => {
asyncTasksArray.forEach((promise, index) => {
//if promise passes
promise.then((val) => {
//store its outcome and increment the count
results[index] = val;
promisesCompleted += 1;
//if all the promises are completed,
//resolve and return the result
if (promisesCompleted === asyncTasksArray.length) {
resolve(results)
}
})
//if any promise fails, reject right away.
.catch(err => {
reject(err)
})
})
});
}
Problem - 9
Write a debounce function with a given delay.
Read the problem statement in detail
function debounce(func, wait) {
// your code here
let timeoutId;
return function(){
const context = this;
const args = arguments
if(timeoutId) clearTimeout(timeoutId)
timeoutId = setTimeout(() => func.apply(context, args), wait)
}
}
Problem - 10
Group Anagram Problem
Anagrams are words that are made of the same characters. So we have to group such words in one group.
Read the problem statement and detailed solution here.
Conclusion
These are the most asked problems in Frontend Interviews in my opinion. For more problems and solutions check the references given below.
Keep practicing ๐ฅ.
References
You made it till the end. Great ๐
Thank you for reading โฅ๏ธ. Don't forget to like and give your feedback. These 2 things help me ๐.
Feel free to reach out on Twitter if you need any guidance further.
Subscribe to my newsletter
Read articles from Faheem Khan directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Faheem Khan
Faheem Khan
Fullstack Web Developer . Interested in Machine Learnig.