OceanofPDF.com JavaScript Interview Guide - Prashant Yadav
OceanofPDF.com JavaScript Interview Guide - Prashant Yadav
com
Introduction
There are many awesome books in the market that help you to get ready
and crack the general Software Engineering or Backend interviews, but a
very few with the focus on the Frontend. This is where the idea to create
this book has hit.
Over 3 years I managed to get a decent exposure to my blog and had around
400 articles written. After getting some good writing experience I decided
to consolidate things in the book format so that It will be easy to refer and
use as a “JavaScript Interview Guide”.
● 20 React questions
ISBN - 978-93-5777-237-2
OceanofPDF.com
First Edition.
© JavaScript Interview Guide | learnersbucket.com
referenced from different articles on the web. I don’t own them, you are free
to use the code examples for your practice, but shall not be reproduced,
transmitted, or sold in whole or in part in any form.
Other than the code, No part of this book may be reproduced or used in any
manner without the written permission of the copyright
books.learnersbucket@gmail.com
FIRST EDITION .
Table of Content
Concepts
1.
Closure
2. Array.reduce() method
3. Promises
1.
Promise.all() polyfill
2.
Promise.any() polyfill
3.
Promise.race() polyfill
4.
Promise.finally() polyfill
5.
Promise.allSettled() polyfill
6.
Custom Promise
7.
8.
9.
React Questions
1.
usePrevious() hook
2.
useIdle() hook
3.
useAsync() hook
4.
useDebounce() hook
5.
useThrottle() hook
6.
useResponsive() hook
7.
useWhyDidYouUpdate() hook
8.
useOnScreen() hook
9.
useScript() hook
1.
2.
10
Concepts
11
Closure
Closures are one of the essential concepts of JavaScript that make the
language very flexible and resilient to work with, having a good
understanding of it is really important.
function
example
() {
let
blog =
"learnersbucket"
function
displayBlog
() {
console
.log(blog);
displayBlog();
example();
// "learnersbucket"
If you notice the variable blog is declared above the function
This is because variables are lexical scope in JavaScript and then can be
accessed anywhere inside that scope unless and until they are
overridden. In the above example, the variable is function scoped and it can
be accessed anywhere within the function body (even in the
inner functions).
function
example
() {
// outer scoped
let
blog =
"learnersbucket"
function
displayBlog
() {
12
let
blog =
"hello"
console
.log(blog);
displayBlog();
example();
// hello
return the inner function, it will have access to the variables (it will be alive)
in the outer scope and perform all sorts of operations.
function
sum
() {
let
a=
10
function
add
){
return
a+b
return
add;
const
fn = sum();
total = fn(
20
);
console
.log(total);
// 30
13
Not only variables but can also access all the other properties and methods
of the outer function, not just parent but at any level in the scope it is
declared.
function
){
function
b
){
function
){
return
a + b + c;
};
return
z;
return
y;
const
a = x(
10
);
// the inner function also accepts an argument and returns the total of both
let
b = a(
20
);
let
c = b(
30
);
console
.log(c);
// 60
If you notice, in this, the inner function has access to the outer
14
Array.reduce() method
// verbose
arr.reduce(callbackfn, initialValue);
// simplified
arr.reduce((
array
) => {
const
return
nextValue;
}, initialValue);
● previousValue – The value returned from the last call of the same function
or the initial value at the beginning.
15
Aggregation
Sum all the elements of the array or multiply all the elements of the array.
const
arr = [
];
const
sum = arr.reduce((
previousValue, currentValue
)
=> {
const
return
nextValue;
},
);
console
.log(sum);
// 10
const
product = arr.reduce((
previousValue, currentValue
=> {
const
return
nextValue;
},
);
console
.log(product);
// 24
Segregation
const
arr = [
1.1
1.2
1.3
2.2
2.3
,
2.4
];
const
segregate = arr.reduce((
previousValue, currentValue
=> {
const
floored =
Math
.floor(currentValue);
if
(!previousValue[floored]){
previousValue[floored] = [];
16
previousValue[floored].push(currentValue);
return
previousValue;
}, {});
console
.log(segregate);
/*
*/
Run in sequence
Let’s say we have an array of functions and a value, the value has to be
passed through these functions in a pipe. Like the initial value has to be
passed to the first function and then the returned value from the first function
has to be passed to the next function and so on.
// functions
const
upperCase = (
str
) => {
return
str.toUpperCase();
};
const
reverse = (
str
) => {
return
str.split(
''
).reverse().join(
''
);
};
const
append = (
str
) => {
return
"Hello "
+ str;
};
const
// initial value
const
initialValue =
"learnersbucket"
17
const
finalValue = arr.reduce((
previousValue, currentElement
)
=> {
const
newValue = currentElement(previousValue);
return
newValue;
}, initialValue);
console
.log(finalValue);
// "Hello TEKCUBSRENRAEL"
const
asyncTask =
function
(
time
){
return
new
Promise
((
resolve, reject
) => {
setTimeout(
()
=> resolve(
`Completing ${time}`
),
100
time
);
});
promises = [
asyncTask(
),
asyncTask(
),
asyncTask(
),
asyncTask(
),
asyncTask(
),
];
const
asyncSeriesExecuter =
function
promises
){
promises.reduce((
acc, curr
) => {
return
acc.then(
()
=> {
return
curr.then(
val
=> {
console
.log(val)});
© JavaScript Interview Guide | learnersbucket.com
18
});
},
Promise
.resolve());
};
asyncSeriesExecuter(promises);
"Completing 3"
"Completing 1"
"Completing 7"
"Completing 2"
"Completing 5"
19
Promises
🤭).
Let us try to get this thing clear and understand promises and how it works.
programming language.
And promises are to help to get out of “callback hell” while dealing with the
asynchronous code and do much more.
promise.
● By the time promises are not fulfilled or rejected, they will be in a pending
state.
● Promises are fulfilled with a certain value, that value can be further
processed (if the value also is a promise) or given back raw.
20
● Promises are rejected with the reason that caused them to be rejected.
● After either of the results, we can also perform the next set of operations.
Anatomy of promise
const
promise =
new
Promise
((
resolve, reject
) => {
// resolve or reject
});
Promise has three methods available to it ( then , catch , & finally ) that can
be used once it is settled ( resolved or rejected ). Each method accepts a
callback function that is invoked depending on the state of the promise.
● finally(onFinallyFn) – This will be called every time after then and catch.
Promise
.prototype.then(onResolvedFn, onRejectedFn);
21
Promise
.prototype.catch(onRejectedFn);
Promise
.prototype.finally(onFinallyFn);
Working of promise
const
promise =
new
Promise
((
resolve, reject
) => {
// 5 second
setTimeout(
()
=> {
resolve(
"Hello World!"
);
},
5000
);
});
console
.log(promise);
/*
Promise { : "pending" }
: "pending"
: Promise.prototype { ... }
*/
setTimeout(
()
=> {
console
.log(promise);
},
6000
);
/*
: "fulfilled"
: "Hello World!"
: Promise.prototype { ... }
*/
22
promise.then((
val
) => {
console
.log(val); });
promise
.then((
val
) => {
return
"ABC "
+ val; })
.then((
val
) => {
console
.log(val); });
// "ABC Hello World!"
promise
.then((
val
) => {
return
"ABC "
+ val; })
.then((
val
) => {
console
.log(val); })
.finally(
()
=> {
console
.log(
"task done"
); });
// "task done"
Similarly, let’s say we reject a promise after 5 seconds, then we can either
use the .then(null, onRejectedFn) or .catch(onRejectedFn) .
const
promise =
new
Promise
((
resolve, reject
) => {
// 5 second
setTimeout(
()
=> {
reject(
"Error 404"
);
},
5000
);
});
promise.then(
null
, (error) => {
console
.error(
, error);
});
promise.catch((
error
) => {
console
.error(
, error);
});
© JavaScript Interview Guide | learnersbucket.com
23
promise.then(
null
, (error) => {
return
error;
}).then((
val
) => {
console
.log(
, val);
});
error
) => {
return
error;
}).then((
val
) => {
console
.log(
, val);
});
promise.then(
null
, (error) => {
return
error;
}).then((
val
) => {
console
.log(
, val);
}).finally(
()
=> {
console
.log(
);
});
promise.catch((
error
) => {
return
error;
}).then((
val
) => {
console
.log(
, val);
}).finally(
()
=> {
console
.log(
);
});
"Error 404"
24
Notice the order of execution, the first error is handled in .then and then in
.catch and then finally blocks of both are called in order.
Helper methods
The promise object has many static methods. Some are helper’s
Promise
.resolve(
"I am resolved"
.then((
val
) => {
console
.log(val); });
// "I am resolved"
Promise
.reject(
.catch((
error
) => {
console
.error(error); });
Process methods
● Promise.all()
● Promise.allSettled()
● Promise.any()
● Promise.race()
25
Async…await
This is new syntax introduced in ES6 that helps to process the promise
better.
const
promise =
Promise
.resolve(
"I am resolved"
);
async
function
example
(promise){
// to handle it better
try
const
resp =
await
promise;
console
.log(resp);
}
catch
(error){
console
.error(error);
finally
console
.log(
"Task done"
);
example(promise);
// "I am resolved"
// "Task done"
To use it we have to mark the function with the async keyword and
then we can use the await keyword inside the async function.
// fat arrow
const
example =
async
() => {
};
const
example =
async
function
(){
26
};
const
promise =
Promise
.resolve(
"I am resolved"
);
// fat arrow
const
example =
async
(promise) => {
// to handle it better
try
const
resp =
await
promise;
return
resp;
}
catch
(error){
console
.error(error);
finally
console
.log(
"Task done"
);
};
console
.log(example(promise));
// "Task done"
example(promise).then((
val
) => {
console
.log(val);
});
//"Task done"
//"I am resolved"
Notice the order of execution here, the try and finally block will be executed,
thus content in the finally block is printed and the value returned is accessed
in the .then that is why "Task done" is printed before "I am resolved" .
27
Javascript.
1. As a normal function.
function
example
(){
console
.log(
"Hello World!"
);
};
example();
// "Hello World!"
2. As a method.
const
obj = {
blog:
"learnersbucket"
displayBlog:
function
(){
console
.log(
this
.blog);
};
obj.displayBlog();
// "learnersbucket"
3. As a constructor.
const
number =
new
Number
"10"
);
console
.log(number);
// 10
28
example
arg1, arg2
){
console
.log(arg1 + arg2);
};
example.call(
undefined
10
20
);
// 30
The value of this is decided upon how the function is invoked, each
invocation creates its own context and the context decides the value of this.
Also the “ strict mode ” affects the behavior of this too.
The value of this in the function invocation refers to the global object.
window in the browser and global in Nodejs.
function
example
(){
console
.log(
this
===
window
);
example();
// true
function
example
(){
this
.blog =
"learnersbucket"
this
.displayBlog =
function
(){
console
.log(
Àwesome ${
this
.blog}`
example();
console
.log(
this
.blog);
// "learnersbucket"
this
.displayBlog();
29
// "Awesome learnersbucket"
Strict mode
If you invoke the function with the strict mode the value of this will be
undefined .
function
example
(){
"use strict"
console
.log(
this
===
undefined
);
}
example();
// true
It also affects all the inner functions that are defined in the function which is
declared in strict mode.
function
example
(){
"use strict"
console
.log(
this
===
undefined
);
// inner function
function
inner
(){
console
.log(
this
===
undefined
);
inner();
example();
// true
// true
30
function thus depending upon the mode, the value of this inside it is decided.
// normal mode
function
example
(){
console
.log(
this
===
window
);
})();
// true
// strict mode
function
example
(){
"use strict"
console
.log(
this
===
undefined
);
})();
// true
When a function is declared inside an object the value of this inside that
function will refer to the object it is declared in.
const
example = {
blog:
'learnersbucket'
displayBlog:
function
(){
console
.log(
this
=== example);
console
.log(
this
.blog);
};
example.displayBlog();
// true
//"learnersbucket"
The context is set at the time of invocation, thus if we update the value of the
object property value, it will be reflected.
const
example = {
31
blog:
'learnersbucket'
displayBlog:
function
(){
console
.log(
this
=== example);
console
.log(
this
.blog);
};
example.blog =
"MDN"
example.displayBlog();
// true
// "MDN"
between both the variables, the original and the one that has the
reference.
const
example = {
blog:
'learnersbucket'
displayBlog:
function
(){
console
.log(
this
=== example);
console
.log(
this
=== example2);
console
.log(
this
.blog);
};
const
example2 = example;
example2.blog =
"MDN"
example.displayBlog();
// true
// true
// "MDN"
example2.displayBlog();
// true
// true
// "MDN"
32
But, if we extract the method and save it in a variable, and then invoke the
variable, the outcome will change.
const
example = {
blog:
'learnersbucket'
displayBlog:
function
(){
console
.log(
this
=== example);
console
.log(
this
.blog);
};
const
display = example.displayBlog;
display();
// false
// undefined
const
example = {
blog:
'learnersbucket'
displayBlog:
function
(){
console
.log(
this
===
window
);
console
.log(
this
.blog);
};
const
display = example.displayBlog;
display();
// true
// undefined
The same happens when you pass the methods to the timers i.e
const
example = {
blog:
'learnersbucket'
displayBlog:
function
(){
console
.log(
this
===
window
);
33
console
.log(
this
.blog);
};
setTimeout(example.displayBlog,
200
);
// true
// undefined
If there are any inner functions inside the methods, the value of this inside
them depends upon how the inner function is invoked.
const
example = {
blog:
'learnersbucket'
displayBlog:
function
(){
function
inner
(){
console
.log(
this
===
window
);
console
.log(
this
.blog);
};
inner();
};
example.displayBlog();
// true
// undefined
Because the inner function is invoked as a normal function the value of this
is a window object.
To access the value of the parent we can use either the Fat arrow
The fat arrow function does not have this of its own, it accesses this in its
nearest scope.
34
In the below example, the fat arrow’s this refers to the this of displayBlog()
which refers to the object it is part of.
const
example = {
blog:
'learnersbucket'
displayBlog:
function
(){
const
inner =
()
=> {
console
.log(
this
=== example);
console
.log(
this
.blog);
};
inner();
};
example.displayBlog();
// true
// "learnersbucket"
const
example = {
blog:
'learnersbucket'
displayBlog:
function
(){
function
inner
(){
console
.log(
this
=== example);
console
.log(
this
.blog);
};
inner.call(
this
);
};
example.displayBlog();
// true
// "learnersbucket"
© JavaScript Interview Guide | learnersbucket.com
35
function
Example
blog
){
this
.blog = blog;
this
.displayBlog =
function
(){
console
.log(
this
.blog);
};
};
const
example =
new
Example(
"learnersbucket"
);
example.displayBlog();
//"learnersbucket"
const
example2 =
new
Example(
"MDN"
);
example2.displayBlog();
//"MDN"
console
const
reg1 =
RegExp
'\\w+'
);
const
reg2 =
RegExp
'\\w+'
);
console
// false
function
Example
blog
){
if
(!(
this
instanceof
Example)) {
throw
Error
);
this
.blog = blog;
};
36
const
example =
new
Example(
"learnersbucket"
);
console
.log(example.blog);
const
example2 = Example(
"MDN"
);
When the function is invoked indirectly the value of this is what is passed as
an argument to the call , apply , & bind method.
Using the call and apply methods we can invoke the function with the new
context. The values will be attached to that execution only.
const
exampleObj = {
blog:
'learnersbucket'
};
function
example
name
){
console
.log(
`${name} runs ${
this
.blog}`
);
};
example.call(exampleObj,
'Prashant'
);
'Prashant'
]);
The difference between call and apply is that apply accepts arguments in an
array, while call accepts it normally.
Permanent binding
When using bind, we can create a new function with the new values
and store it in a variable, and then use it further. It creates fresh permanent
binding without affecting the original function.
37
const
exampleObj = {
name:
'prashant'
};
function
example
(
blog
){
console
.log(
`${
this
);
};
const
bounded = example.bind(exampleObj);
bounded(
'learnersbucket'
);
bounded(
'MDN'
);
JavaScript Questions
39
Promise.all() polyfill
Definition
According to MDN –
After reading the definition of Promise.all() we can break down the problem
in sub-problem and tackle it one by one.
● The promise will resolve with the result of all the passed
promise.
● The results are returned in the same order as the promises are in the given
array.
Implementation
const
myPromiseAll =
function
(
taskList
){
const
results = [];
let
promisesCompleted =
return
new
Promise
((
resolve, reject
) => {
taskList.forEach((
promise, index
) => {
promise.then((
val
) => {
results[index] = val;
promisesCompleted +=
40
if
resolve(results)
})
//if any promise fails, reject.
.catch(
error
=> {
reject(error)
})
})
});
Test case 1
Input:
function
task
time
){
return
new
Promise
(
function
resolve, reject
){
setTimeout(
function
() {
resolve(time);
}, time);
});
const
taskList = [task(
1000
), task(
5000
), task(
3000
)];
//run promise.all
myPromiseAll(taskList)
.then(
results
=> {
console
.log(
"got results"
, results)
})
.catch(
console
.error);
Output:
Test case 2
Input:
function
task
time
){
return
new
Promise
function
resolve, reject
){
41
setTimeout(
function
() {
if
(time <
3000
){
reject(
"Rejected"
);
else
resolve(time);
}, time);
});
const
taskList = [task(
1000
), task(
5000
), task(
3000
)];
//run promise.all
myPromiseAll(taskList)
.then(
results
=> {
console
.log(
"got results"
, results)
})
.catch(
console
.error);
Output:
"Rejected"
42
Promise.any() Polyfill
Definition
According to MDN –
promise.
promises resolves.
● Else if all of the input promises are rejected then the returned promise is
rejected with the array of all the input promises
reasons.
Implementation
const
any =
function
promisesArray
){
const
promiseErrors =
new
Array
(promisesArray.length);
let
counter =
return
new
Promise
((
resolve, reject
) => {
promisesArray.forEach((
promise
) => {
Promise
.resolve(promise)
.then(resolve)
43
.catch((
error
) => {
promiseErrors[counter] = error;
counter = counter +
if
promise
reject(promiseErrors);
});
});
});
};
Test case 1
Input:
const
test1 =
new
Promise
function
resolve, reject
setTimeout(reject,
500
'one'
);
});
const
test2 =
new
Promise
function
resolve, reject
setTimeout(resolve,
600
'two'
);
});
const
test3 =
new
Promise
function
(
resolve, reject
setTimeout(reject,
200
'three'
);
});
function
value
){
console
.log(value);
}).catch(
function
(
err
){
console
.log(err);
});
Output:
"two"
Test case 2
Input:
const
test1 =
new
Promise
function
resolve, reject
{
© JavaScript Interview Guide | learnersbucket.com
44
setTimeout(reject,
500
'one'
);
});
const
test2 =
new
Promise
function
resolve, reject
setTimeout(reject,
600
,
'two'
);
});
const
test3 =
new
Promise
function
resolve, reject
setTimeout(reject,
200
'three'
);
});
any([test1, test2, test3]).then(
function
value
){
console
.log(value);
}).catch(
function
err
){
console
.log(err);
});
Output:
"three"
,
"one"
"two"
45
Promise.race() polyfill
Definition
According to MDN –
● It returns a promise.
Implementation
const
race =
function
promisesArray
){
return
new
Promise
((
resolve, reject
) => {
promisesArray.forEach((
promise
) => {
Promise
.resolve(promise)
// resolve, when any of the input promise resolves
.then(resolve, reject)
.catch(reject);
});
});
};
46
Test case 1
Input:
const
test1 =
new
Promise
function
resolve, reject
)
{
setTimeout(resolve,
500
'one'
);
});
const
test2 =
new
Promise
function
resolve, reject
setTimeout(resolve,
100
,
'two'
);
});
const
test3 =
new
Promise
function
resolve, reject
setTimeout(reject,
200
'three'
);
});
value
faster
console
.log(value);
}).catch(
function
err
){
console
.log(err);
});
Output:
"two"
Test case 2
Input:
const
test1 =
new
Promise
function
resolve, reject
setTimeout(resolve,
500
'one'
);
});
const
test2 =
new
Promise
function
resolve, reject
setTimeout(resolve,
100
'two'
);
});
const
test3 =
new
Promise
function
(
resolve, reject
setTimeout(reject,
40
'three'
);
});
function
value
faster
console
.log(value);
}).catch(
function
err
){
console
.log(err);
});
Output:
"three"
48
Promise.finally() polyfill
Definition
According to MDN –
Same as the try … catch … finally block where no matter whether code runs
in a try block or catch block, the finally block will always be executed at the
end, which can be used for cleanup operations.
The same way for Promises we have .then() for when promise resolves and
.catch() for when promise rejects and .finally() block which will always run
after any of those.
Example
Input:
function
checkMail
() {
return
new
Promise
((
resolve, reject
) => {
if
Math
.random() >
0.5
){
resolve(
'Mail has arrived'
);
else
reject(
new
Error
'Failed to arrive'
));
});
checkMail()
.then((
) => {
console
.log(mail);
})
.catch((
err
) => {
console
.error(err);
})
49
.finally(
()
=> {
console
.log(
'Experiment completed'
);
});
Output:
Error
: Failed to arrive
"Experiment completed"
argument.
Implementation
Promise
.prototype.finally =
function
callback
){
if
typeof
callback !==
'function'
){
return
this
.then(callback, callback);
const
P=
this
.constructor ||
Promise
return
this
.then(
value
=> P.resolve(callback()).then(
()
=> value),
()
=> {
throw
err; })
);
};
50
Test Case
Input:
const
logger = (
)) => (...values)
=> {
console
.log(label, ...values,
àfter ${
Date
.now()
- start}ms`
);
};
const
delay = (
value, ms
) =>
new
Promise
resolve
=>
function
test
impl
){
const
log =
ordinal
`${ordinal}
${impl} ${state}`
);
const
first = log(
'first'
);
delay(
2
,
1000
.finally(first(
'settled'
))
.then(first(
'fulfilled'
), first(
'rejected'
));
const
second = log(
'second'
);
delay(
Promise
.reject(
3
),
2000
.finally(second(
'settled'
))
.then(second(
'fulfilled'
), second(
'rejected'
));
const
third = log(
'third'
);
delay(
3000
)
.finally(third(
'settled'
))
.finally(
()
=> delay(
500
))
.then(third(
'fulfilled'
), third(
'rejected'
));
const
fourth = log(
'fourth'
);
// test adoption of rejected promise
delay(
4000
.finally(fourth(
'settled'
))
.finally(
()
=> delay(
Promise
.reject(
),
500
))
.then(fourth(
'fulfilled'
), fourth(
'rejected'
));
51
test(
'polyfill'
);
Output:
"after 1005ms"
"after 1007ms"
"after 2006ms"
"after 2008ms"
"third polyfill settled"
"after 3006ms"
"after 3512ms"
"after 4000ms"
"after 4506ms"
Edge Case
Promise
.resolve(
).then(
()
val
)
=>
console
.log(val)});
// undefined
Promise
.resolve(
).finally(
()
=> {}).then((
val
) =>
console
.log(val)});
// 2
Promise
.reject(
).then(
()
val
=>
console
.log(val)});
// undefined
Promise
.reject(
).finally(
()
=> {}).then((
val
) =>
console
.log(val)});
// 3
//A throw (or returning a rejected promise) in the finally callback will reject
//the new promise with the rejection reason specified when calling throw()
Promise
.reject(
).finally(
()
=> {
throw
'Parameter
is not a number!'
}).then((
val
) => {
console
.log(val)});
// 'Parameter is not a number!'
52
Promise.allSettled() polyfill
Definition
According to MDN –
The Promise.allSettled() method returns a promise that fulfills after all of the
given promises have either fulfilled or rejected, with an array of objects that
each describes the outcome of each promise.
returns an array with the result of all the promises whether they
Implementation
const
allSettled = (
promises
) => {
const
mappedPromises = promises.map((
p)
=>
Promise
.resolve(p)
.then(
val
=> ({ status:
'fulfilled'
'rejected'
, reason: err })
);
Promise
.all(mappedPromises);
53
Test Case
Input:
const
a=
new
Promise
((
resolve
) => setTimeout(
()
=>
{ resolve(
) },
200
));
const
b=
new
Promise
((
resolve,reject
) => reject(
));
const
c=
new
Promise
((
resolve
) => resolve(
));
allSettled([a, b, c]).then((
val
)=> {
console
.log(val)});
Output:
"status"
"fulfilled"
"value"
},
"status"
"rejected"
,
"reason"
},
"status"
"fulfilled"
"value"
54
Custom promise
Problem Statement -
object.
Anatomy of Promise
const
promise =
new
Promise
((
resolve, reject
) => {
resolve(value);
// this will reject the promise
reject(reason);
});
promise.then((
value
) => {
});
promise.catch((
value
) => {
});
55
promise.finally((
value
) => {
});
Working of Promise
const
promise =
new
Promise
((
resolve, reject
) => {
setTimeout(
()
=> {
resolve(
"hello"
);
},
1000
);
});
promise.then((
value
) => {
console
.log(value);
});
Use two handlers onSuccess and onError and assign this them to the
Whenever the resolve or reject methods are invoked, run all the handlers in
sequence and pass down the values to the next.
// enum of states
const
states = {
PENDING:
FULFILLED:
REJECTED:
2
class
MyPromise
constructor
(callback) {
this
.state = states.PENDING;
56
this
.value =
undefined
this
.handlers = [];
try
{
callback(
this
._resolve,
this
._reject);
catch
(error) {
this
._reject(error);
_resolve = (
value
) => {
this
._handleUpdate(states.FULFILLED, value);
value
) => {
this
._handleUpdate(states.REJECTED, value);
_handleUpdate = (
state, value
) => {
if
return
setTimeout(
()
=> {
if
(value
instanceof
MyPromise) {
value.then(
this
._resolve,
this
._reject);
this
.state = state;
this
.value = value;
this
._executeHandlers();
},
()
=> {
if
this
57
return
this
.handlers.forEach((
handler
) => {
if
this
handler.onSuccess(
this
.value);
return
handler.onFailure(
this
.value);
})
this
.handlers = [];
// add handlers
_addHandler = (
handler
) => {
this
.handlers.push(handler);
this
._executeHandlers();
// then handler
then = (
onSuccess, onFailure
) => {
return
new
MyPromise((
resolve, reject
) =>
this
._addHandler({
onSuccess: (
value
) => {
if
(!onSuccess) {
return
resolve(value);
try
return
resolve(onSuccess(value));
catch
(error) {
reject(error);
},
onFailure: (
value
) => {
if
(!onFailure) {
return
reject(value);
58
try
return
reject(onFailure(value));
catch
(error) {
return
reject(error);
})
})
};
catch
=(
onFailure
) => {
return
this
.then(
null
, onFailure);
};
finally
=(
callback
) => {
new
MyPromise((
resolve, reject
) =>
let
wasResolved;
let
value;
this
.then((
val
) => {
value = val;
wasResolved =
true
return
callback();
}).catch((
err
) => {
value = err;
wasResolved =
false
return
callback();
})
if
(wasResolved) {
resolve(value);
else
reject(value);
})
};
};
Test Case
Input:
const
promise =
new
MyPromise((
resolve, reject
) =>
setTimeout(
()
=> {
resolve(
"hello"
);
},
1000
);
});
promise.then((
value
) => {
console
.log(value);
});
Output:
"hello"
60
Problem Statement -
Example
Input:
asyncTask(
3
),
asyncTask(
),
asyncTask(
Output:
For…of loop allows using await keyword performing the next iteration only
when the previous one is finished.
Implementation
const
asyncSeriesExecuter =
async
function
promises
){
for
let
promise
of
promises) {
try
const
result =
await
promise;
console
.log(result);
catch
(e){
console
.log(e);
Test Case
Input:
const
asyncTask =
function
){
return
new
Promise
((
resolve, reject
) => {
setTimeout(
()
=> resolve(
`Completing ${i}`
),
100
*i)
});
const
promises = [
asyncTask(
),
asyncTask(
1
),
asyncTask(
),
asyncTask(
),
asyncTask(
),
];
asyncSeriesExecuter(promises);
Output:
"Completing 3"
"Completing 1"
"Completing 7"
"Completing 2"
"Completing 5"
Implementation
const
asyncSeriesExecuter =
function
promises
){
let
promise = promises.shift();
promise.then((
data
) => {
console
.log(data);
if
(promises.length >
){
asyncSeriesExecuter(promises);
});
Test Case
Input:
const
asyncTask =
function
){
return
new
Promise
((
resolve, reject
) => {
setTimeout(
()
=> resolve(
`Completing ${i}`
),
100
*i)
});
const
promises = [
asyncTask(
),
asyncTask(
1
),
asyncTask(
),
asyncTask(
),
asyncTask(
),
];
asyncSeriesExecuter(promises);
63
Output:
"Completing 3"
"Completing 1"
"Completing 7"
"Completing 2"
"Completing 5"
Approach 3 - Using Array.reduce()
Implementation
const
asyncSeriesExecuter =
function
promises
){
promises.reduce((
acc, curr
) => {
return
acc.then(
()
=> {
return
curr.then(
val
=> {
console
.log(val)});
});
},
Promise
.resolve());
Test Case
Input:
const
asyncTask =
function
){
return
new
Promise
((
resolve, reject
) => {
setTimeout(
()
=> resolve(
`Completing ${i}`
),
100
*i)
});
const
promises = [
asyncTask(
),
asyncTask(
),
asyncTask(
7
),
asyncTask(
),
asyncTask(
),
];
64
asyncSeriesExecuter(promises);
Output:
"Completing 3"
"Completing 1"
"Completing 7"
"Completing 2"
"Completing 5"
65
Execute async functions in Parallel
Problem Statement -
Example
Input:
executeParallel(
[asyncTask(
), asyncTask(
), asyncTask(
)],
(result) => {
console
.log(result);});
Output:
[
2
We can use the simple forEach loop on each task and execute them
tasks that have been finished so that when each task is executed, we can
invoke the callback function.
Invoking the callback function at the end of the loop won’t work as the
async tasks may finish at different intervals.
function
asyncParallel
tasks, callback
){
const
results = [];
tasksCompleted =
tasks.forEach(
asyncTask
=> {
66
asyncTask(
value
=> {
results.push(value);
tasksCompleted++;
// if all tasks are executed
if
callback(results);
});
});
};
Test Case
callback and runs a setTimeout for a random time and invokes this
function
createAsyncTask
() {
const
value =
Math
.floor(
Math
.random() *
10
);
return
function
callback
){
setTimeout(
()
=> {
callback(value);
}, value *
1000
);
};
Using this we can create multiple tasks and then test the async
parallel.
Input:
const
taskList = [
createAsyncTask(),
createAsyncTask(),
67
createAsyncTask(),
createAsyncTask(),
createAsyncTask(),
createAsyncTask()
];
console
.log(
'results'
, result);
});
Output:
"results"
// [object Array] (6)
68
Problem Statement -
Example
Input:
retry(asyncFn, retries =
, delay =
50
, finalError
'Failed'
);
Output:
... attempt
-> failed
... attempt
50
ms -> failed
... attempt
3
-> retry after
50
ms -> failed
... Failed.
In short, we have to create a retry function that Keeps on retrying until the
promise resolves with delay and max retries.
We will see two code examples one with thenable promise and the second
with async…await .
Let us first create the delay function so that we can take a pause for a
specified amount of time.
Delay function
We can create a delay function by creating a new promise and resolve it after
given time using setTimeout .
//delay func
const
wait =
ms
=>
new
Promise
((
resolve
) => {
setTimeout(
()
});
69
To retry the promise we have to call the same function recursively with
reduced max tries, if the promise fails then in the .catch() method check if
there are number of tries left then recursively call the same function or else
reject with the final error.
Implementation
const
retryWithDelay = (
operation,
retries =
delay =
50
,
finalErr =
'Retry failed'
) =>
new
Promise
((
resolve, reject
=> {
return
operation()
.then(resolve)
.catch((
reason
) => {
if
(retries >
){
//delay the next call
return
wait(delay)
.then(
retryWithDelay.bind(
null
operation,
retries -
delay,
finalErr)
.then(resolve)
.catch(reject);
reject(finalErr);
});
70
});
Test Case
Input:
// Test function
const
getTestFunc =
()
=> {
let
callCounter =
return
async
() => {
callCounter +=
// throw error
if
(callCounter <
){
throw
new
Error
'Not yet'
);
}
// Test the code
const
test =
async
() => {
await
retryWithDelay(getTestFunc(),
10
);
console
.log(
'success'
);
await
retryWithDelay(getTestFunc(),
);
console
.log(
test().catch(
console
.error);
Output:
"success"
// 1st test
"Retry failed"
//2nd test
71
the error, thus in the catch block, we can check if the max retries are still left
then recursively call the same function or else throw the final error.
Implementation
const
retryWithDelay =
async
fn, retries =
, interval =
50
finalErr =
'Retry failed'
) => {
try
// try
await
fn();
catch
(err) {
// if no retries left
// throw error
if
(retries <=
){
return
Promise
.reject(finalErr);
await
wait(interval);
return
retryWithDelay(fn, (retries -
), interval,
finalErr);
Test Case
Input:
// Test function
const
getTestFunc =
()
=> {
let
callCounter =
return
async
() => {
callCounter +=
// throw error
if
(callCounter <
5
){
throw
new
Error
'Not yet'
);
72
const
test =
async
() => {
await
retryWithDelay(getTestFunc(),
10
);
console
.log(
'success'
);
await
retryWithDelay(getTestFunc(),
);
console
.log(
);
test().catch(
console
.error);
Output:
"success"
// 1st test
"Retry failed"
//2nd test
Alternatively, you can also update the code and keep on retrying the API call
based on some test.
73
Problem Statement -
rejects it if any error occurs. The inputs are run in a sequence that is one
after another.
finished processing, the first argument of the callback will be the error flag
and the second will be the result.
Example
Input:
let
numPromise = mapSeries([
],
function
num, callback
){
setTimeout(
function
() {
num = num *
console
.log(num);
// throw error
if
(num ===
12
){
callback(
true
);
else
callback(
null
, num);
}
},
1000
);
});
numPromise
.then((
result
) =>
console
.log(
"success:"
+ result))
.catch(
()
=>
console
.log(
"no success"
));
Output:
10
"success:2,4,6,8,10"
after last
Async.series() , the only difference will be that each input will be passed to
the asynchronous iteratee function and the processed output from it will be
returned in the output array.
We will return a new promise and inside this promise, iterate each
accumulator to the Array.reduce() and inside the reduce, once the previous
promise is resolved listen to it, create a new promise and pass the input value
to the asynchronous iteratee function, based on the
callback result from this iteratee function, either resolve or reject the current
promise.
This will go in sequence one after another.
const
mapSeries = (
arr, fn
) => {
return
new
Promise
((
resolve, reject
) => {
const
output = [];
// run it in series
75
const
final = arr.reduce((
acc, current
) => {
return
acc.then((
val
) => {
return
new
Promise
((
resolve, reject
) => {
// resolve or reject
fn(current, (error, result) => {
if
(error){
reject(error);
else
resolve([...val, result]);
});
});
});
},
Promise
.resolve([]));
final
.then((
result
) => {
resolve(result);
})
.catch((
error
) => {
reject(error);
});
});
};
Input:
let
numPromise = mapSeries([
,
4
],
function
num, callback
){
setTimeout(
function
() {
num = num *
console
.log(num);
// throw error
if
(num ===
12
){
76
callback(
true
);
else
callback(
null
, num);
},
1000
);
});
numPromise
.then((
result
) =>
console
.log(
"success:"
+ result))
.catch(
()
=>
console
.log(
"no success"
));
Output:
10
"success:2,4,6,8,10"
// this will be printed immediately
after last
Input:
let
numPromise = mapSeries([
],
function
num, callback
){
setTimeout(
function
() {
num = num *
console
.log(num);
// throw error
if
(num ===
){
callback(
true
);
else
callback(
null
, num);
},
2000
);
});
numPromise
.then((
result
) =>
console
.log(
"success:"
+ result))
.catch(
()
=>
console
.log(
"no success"
));
77
Output:
"no success"
last
78
Problem Statement -
finished processing, the first argument of the callback will be the error flag
and the second will be the result.
Example
Input:
let
numPromise = mapLimit([
],
function
(
num, callback
){
setTimeout(
function
() {
num = num *
console
.log(num);
callback(
null
, num);
},
1000
);
});
numPromise
.then((
result
) =>
console
.log(
"success:"
+ result))
.catch(
()
=>
console
.log(
"no success"
));
Output:
79
10
● First chop the input array into the subarrays of the given limit.
This will return us an array of arrays like [[1, 2, 3], [4, 5]] .
● The parent array will run in series, that is the next subarray will execute
only after the current subarray is done.
● Accumulate all the results of each sub-array element and resolve the
promise with this.
Array
.prototype.chop =
function
(
size
){
//temp array
const
temp = [...this];
if
(!size) {
return
temp;
//output
const
output = [];
let
i=
while
(i < temp.length) {
output.push(temp.slice(i, i + size));
i = i + size;
80
return
output;
};
const
mapLimit = (
arr, limit, fn
) => {
return
new
Promise
((
resolve, reject
) => {
let
chopped = arr.chop(limit);
// run it in series
on to the next
const
final = chopped.reduce((
a, b
) => {
return
a.then((
val
) => {
and resolve
return
new
Promise
((
resolve, reject
) => {
const
results = [];
let
tasksCompleted =
b.forEach((
e
) => {
// reject or resolve
if
(error){
reject(error);
else
results.push(value);
81
tasksCompleted++;
if
resolve([...val, ...results]);
}
}
});
});
});
});
},
Promise
.resolve([]));
final
.then((
result
) => {
resolve(result);
})
.catch((
) => {
reject(e);
});
});
};
Input:
let
numPromise = mapLimit([
],
function
(
num, callback
){
setTimeout(
function
() {
num = num *
console
.log(num);
callback(
null
, num);
},
2000
);
});
numPromise
OceanofPDF.com
.then((
result
) =>
console
.log(
"success:"
+ result))
.catch(
()
=>
console
.log(
"no success"
));
Output:
// first batch
82
4
6
// second batch
10
"success:2,4,6,8,10"
Input:
let
numPromise = mapLimit([
],
3
,
function
num, callback
){
setTimeout(
function
() {
num = num *
console
.log(num);
// throw error
if
(num ===
){
callback(
true
);
else
callback(
null
, num);
},
2000
);
});
numPromise
.then((
result
) =>
console
.log(
"success:"
+ result))
.catch(
()
=>
console
.log(
"no success"
));
Output:
// first batch
"no success"
83
Problem Statement -
iteratee function and returns a promise that resolves with the list of inputs
that has passed the test through the iteratee function.
The inputs will run in parallel, but the output will be in the same order as the
original.
finished processing, the first argument of the callback will be the error flag
and the second will be the result.
Example
Input:
let
numPromise = filter([
],
function
(
num, callback
){
setTimeout(
function
() {
num = num *
console
.log(num);
// throw error
if
(num ===
){
callback(
true
);
}
else
callback(
null
, num !==
);
},
2000
);
});
numPromise
.then((
result
) =>
console
.log(
"success:"
+ result))
.catch(
()
=>
console
.log(
"no success"
));
Output:
84
10
"success:1,3,4,5"
To implement this we will create a new promise and return it, inside this
promise, run all the input values in parallel using the
Array.forEach() and inside the forEach pass each value to the iteratee
function.
Inside the iteratee function’s callback if the result is true which means the
input has passed the test, then add that input to the result at the current index.
In the end, if we are at the last element of the input array, resolve the
promise with the result.
Filter the final output to remove the gaps as we have to maintain the order
for the passed values. There will be no value at the indexes of unpassed
values in the output array.
const
filter = (
arr, fn
) => {
return
new
Promise
((
resolve, reject
) => {
const
output = [];
let
track =
0
arr.forEach((
e, i
) => {
// reject on error
if(error){
85
reject(error);
track++;
if
(result){
output[i] = e;
}
// if the last element of the input array
if
resolve(output.filter(
Boolean
));
});
});
});
};
Test Case
Input:
let
numPromise = filter([
2
,
],
function
num, callback
){
setTimeout(
function
() {
num = num *
console
.log(num);
// throw error
if
(num ===
){
callback(
true
);
else
callback(
null
, num !==
);
},
2000
);
});
© JavaScript Interview Guide | learnersbucket.com
86
numPromise
.then((
result
) =>
console
.log(
"success:"
+ result))
.catch(
()
=>
console
.log(
"no success"
));
Output:
4
6
10
"success:1,3,4,5"
87
Problem Statement -
iteratee function and returns a promise that resolves with the list of inputs
that has failed the test through the iteratee function. This
The inputs will run in parallel, but the output will be in the same order as the
original.
finished processing, the first argument of the callback will be the error flag
and the second will be the result.
Example
Input:
let
numPromise = reject([
1
],
function
num, callback
){
setTimeout(
function
() {
num = num *
;
console
.log(num);
// throw error
if
(num ===
){
callback(
true
);
else
callback(
null
, num !==
);
},
2000
);
});
numPromise
.then((
result
) =>
console
.log(
"success:"
+ result))
.catch(
()
=>
console
.log(
"no success"
));
88
Output:
10
"success:2"
We can use the exact same implementation of the Async filter, only
changing the logic to add the value to the output array only when it fails the
test.
const
reject = (
arr, fn
) => {
return
new
Promise
((
resolve, reject
) => {
const
output = [];
let
track =
arr.forEach((
e, i
) => {
// reject on error
if
(error){
reject(error);
track++;
if
(!result){
output[i] = e;
if
resolve(output.filter(
Boolean
));
89
});
});
});
};
Test Case
let
numPromise = reject([
],
function
num, callback
){
setTimeout(
function
() {
num = num *
2
;
console
.log(num);
// throw error
if
(num ===
){
callback(
true
);
else
callback(
null
, num !==
);
}
},
2000
);
});
numPromise
.then((
result
) =>
console
.log(
"success:"
+ result))
.catch(
()
=>
console
.log(
"no success"
));
Output:
2
10
"success:2"
90
Problem Statement -
Given a list of promises and their priorities, call them parallelly and resolve
with the value of the first promise with the most priority. If all the promises
fail then reject with a custom error.
Example
const
promises = [
{status:
'resolve'
, priority:
},
{status:
'reject'
, priority:
},
{status:
'resolve'
, priority:
},
{status:
'reject'
, priority:
];
resolvePromisesWithPriority(promises);
While executing, use a variable to track the most priority promise, if any
promise rejects and it is the promise of most priority, update the variable to
the next in priority.
In the end, if any promise resolves and it is of most priority, resolve it, else if
all the promises fail then reject with the custom error.
To track if all the promises are finished, we will use another variable named
taskCompleted .
91
function
resolvePromisesWithPriority
promises
){
promises.sort((
a, b
let
rejected = {};
// track the result
let
result = {};
let
mostPriority =
let
taskCompleted =
return
new
Promise
((
resolve, reject
) => {
// run each task in parallel
promises.forEach((
{task, priority}, i
) => {
task.then((
value
) => {
result[priority] = value;
}).catch((
error
) => {
reference
rejected[priority] =
true
if
mostPriority++;
}).finally(
()
=> {
if
promises[mostPriority].priority){
92
console
.log(rejected);
resolve(priority);
}
taskCompleted++;
resolved
if
reject(
);
});
});
});
};
Test Case
Input:
function
createAsyncTask
() {
const
value =
Math
.floor(
Math
.random() *
10
);
return
new
Promise
((
resolve, reject
) => {
setTimeout(
()
=> {
if
(value <
){
reject();
else
resolve(value);
}, value *
100
);
});
};
const
promises = [
1
},
},
},
];
93
resolvePromisesWithPriority(promises).then((
result
)=>{
console
.log(result);
}, (error) => {
console
.log(error);
});
Output:
/*
// log
"1": true,
"3": true,
"4": true
// log
*/
94
Problem Statement -
In the dependencies list, we will check if all the tasks in the list are
completed or not, if it is completed then filter them out as we no
Otherwise, if there are dependencies pending, push them into a list, and
execute them one by one. Once all are completed, invoke the
callback.
Use a flag to determine the state of the task, i.e completed or not.
class
Task
constructor
this
dependency
=> dependency
instanceof
: [];
this
.currentDependencyCount =
this
.dependencies.length;
// the callback
this
.job = job;
95
this
.isCompleted =
false
// to execute is sequence
this
.subscribedList = [];
this
.processJob();
processJob() {
// if there is dependency
if
this
.dependencies &&
this
.dependencies.length)
{
for
let
dependency
of
this
.dependencies)
dependency.subscribe(
this
.trackDependency.bind(
this
));
else
this
.job(
this
.done.bind(
this
));
trackDependency() {
this
.currentDependencyCount--;
if
this
.currentDependencyCount ===
){
this
.job(
this
.done.bind(
this
));
subscribe(cb) {
this
.subscribedList.push(cb);
96
// mark it as complete
// to print it in sequence
done() {
this
.isCompleted =
true
;
for
const
callback
of
this
.subscribedList)
callback();
Test Case
Input:
const
processA =
new
Task(
null
, (done) => {
setTimeout(
()
=> {
console
.log(
'Process A'
);
done();
},
100
);
});
const
processB =
new
setTimeout(
()
=> {
console
.log(
'Process B'
);
done();
},
1500
);
});
const
processC =
new
Task(
null
, (done) => {
setTimeout(
()
=> {
console
.log(
'Process C'
);
done();
},
1000
);
});
const
processD =
new
=> {
setTimeout(
()
=> {
console
.log(
'Process D'
);
done();
},
1000
);
});
97
const
processE =
new
=> {
setTimeout(
()
=> {
console
.log(
'Process E'
);
done();
},
100
);
});
const
createAllDoneInstance = (
allDoneCallback
) =>
new
Task([processA,
createAllDoneInstance((
done
) => {
console
.log(
'All is done!'
);
done();
});
Output:
"Process A"
"Process C"
"Process B"
"Process D"
"Process E"
"All is done!"
98
Problem Statement -
initial value and steps as input and increments the initial value with given
steps every second. The incrementer can be paused and resumed
back.
It is one of the classic problems which use two of the trickiest concepts of
JavaScript.
1. Timers.
2. Closure.
Use the setInterval to auto increment the values, whereas wrapping the start
and stop function inside the closure and returning them, so that the
incrementor can be controlled while still maintaining the
value.
Defining the function body
Our incrementor function will take two inputs, initial value, and steps.
2. For storing the IntervalId, so that it can be cleared to stop the timer and
resume when needed.
const
timer = (
init = 0, step = 1
) => {
let
intervalId;
let
count = init;
99
Start function
second, and in each interval call, the initial value will be increased by the
given step and it will be logged in the console.
setInterval’s id will be stored In the intervalId variable.
const
startTimer =
()
=> {
if
(!intervalId){
intervalId = setInterval(
()
=> {
console
.log(count);
count += step;
},
1000
);
Stop function
Inside the stop function we stop the increment by invoking the
intervalId to null.
const
stopTimer =
()
=> {
clearInterval(intervalId);
intervalId =
null
return
startTimer,
stopTimer,
};
100
Implementation
const
timer = (
init = 0, step = 1
) => {
let
intervalId;
let
count = init;
const
startTimer =
()
=> {
if
(!intervalId){
intervalId = setInterval(
()
=> {
console
.log(count);
count += step;
},
1000
);
const
stopTimer =
()
=> {
clearInterval(intervalId);
intervalId =
null
return
startTimer,
stopTimer,
};
}
Test Case
Input:
const
timerObj = timer(
10
10
);
//start
timerObj.startTimer();
//stop
setTimeout(
()
=> {
timerObj.stopTimer();
},
5000
);
Output:
10
20
30
40
101
Problem Statement -
Implement queue data structure using two different stacks instead of using
an array or object as a linked list.
Every time an item is added to the queue first copy all the items from the
stack1 to the stack2 then add the new element to the stack2.
Then again copy all the items back from stack2 to stack1.
This way we keep FIFO principal in place by keeping the first element at
front and last element at end.
102
Implementation
//Enqueue costly
class
QueueUsingStack
constructor
() {
this
.stack1 =
new
Stack();
this
.stack2 =
new
Stack();
enqueue = (
) => {
while
(!
this
.stack1.isEmpty()){
this
.stack2.push(
this
.stack1.pop());
this
.stack2.push(x);
while
(!
this
.stack2.isEmpty()){
this
.stack1.push(
this
.stack2.pop());
dequeue =
()
=> {
return
this
.stack1.pop();
peek =
()
=> {
return
this
.stack1.peek();
size =
()
=> {
return
this
.stack1.size();
isEmpty =
()
=> {
return
this
.stack1.isEmpty();
clear =
()
=> {
this
.stack1 =
new
Stack();
this
.stack2 =
new
Stack();
103
Test Case
Input:
const
queue =
new
QueueUsingStack();
queue.enqueue(
10
);
queue.enqueue(
20
);
queue.enqueue(
30
);
queue.enqueue(
40
);
queue.enqueue(
50
);
console
.log(queue.peek());
console
.log(queue.dequeue());
console
.log(queue.peek());
console
.log(queue.dequeue());
console
.log(queue.peek());
Output:
10
10
20
20
30
This is the most efficient way because once we have stored the data in an
organized way all other operations work perfectly, we don’t need to change
them.
In this we will store the element in LIFO order using stack, but while
removing the element we will have to copy all the elements into
another stack so that the first element is at the top and can be
removed.
And then copy all the elements back to the first stack.
The only problem with this way is that for the peek operation we have to do
the same process, this makes two operations run in O(n) time
It is always better to store the data in an organized way so that all the later
processes don’t get affected.
Implementation
class
QueueUsingStack
constructor
() {
this
.stack1 =
new
Stack();
this
.stack2 =
new
Stack();
enqueue = (
) => {
this
.stack1.push(x);
dequeue =
()
=> {
while
(!
this
.stack1.isEmpty()){
this
.stack2.push(
this
.stack1.pop());
const
item =
this
.stack2.pop();
while
(!
this
.stack2.isEmpty()){
this
.stack1.push(
this
.stack2.pop());
return
item;
peek =
()
=> {
while
(!
this
.stack1.isEmpty()){
this
.stack2.push(
this
.stack1.pop());
const
item =
this
.stack2.peek();
while
(!
this
.stack2.isEmpty()){
this
.stack1.push(
this
.stack2.pop());
return
item;
size =
()
=> {
return
this
.stack1.size();
isEmpty =
()
=> {
return
this
.stack1.isEmpty();
clear =
()
=> {
this
.stack1 =
new
Stack();
this
.stack2 =
new
Stack();
Test Case
Input:
const
queue =
new
QueueUsingStack();
queue.enqueue(
10
);
queue.enqueue(
20
);
queue.enqueue(
30
);
queue.enqueue(
40
);
queue.enqueue(
50
);
console
.log(queue.peek());
console
.log(queue.dequeue());
console
.log(queue.peek());
console
.log(queue.dequeue());
console
.log(queue.peek());
Output:
10
10
20
20
30
106
Problem Statement -
Approach
● Every time we will add new data into the queue, we will move the existing
data behind new data by repeatedly removing the first
Implementation
function
Stack
() {
let
queue =
new
Queue();
//Push
this
.push =
function
elm
){
let
size = queue.size();
queue.enqueue(elm);
for
let
i=
x = queue.dequeue();
queue.enqueue(x);
//Pop
this
.pop =
function
(){
if
(queue.isEmpty()){
return
null
return
queue.dequeue();
//Peek
this
.peek =
function
(){
if
(queue.isEmpty()){
return
null
return
queue.front();
//Size
this
.size =
function
(){
return
queue.size();
//IsEmpty
this
.isEmpty =
function
(){
return
queue.isEmpty();
//Clear
this
.clear =
function
(){
queue.clear();
return
true
;
}
//ToArray
this
.toArray =
function
(){
return
queue.toArray();
Test Case
Input:
let
stack =
new
Stack();
of Stack
stack.push(
1
);
stack.push(
);
stack.push(
);
console
.log(stack.peek());
console
.log(stack.isEmpty());
108
console
.log(stack.size());
console
.log(stack.pop());
console
.log(stack.toArray());
console
.log(stack.size());
stack.clear();
console
.log(stack.isEmpty());
Output:
false
true
109
Implement a stack data structure in which we can get the max and min value
through function in O(1) time.
Example
Input:
17
23
88
54
22
Output:
max:
88
min:
Approach
● If it is empty then add the current value as current , max and min values.
● Else get the previous value and compare it with the current item, if it is
greater than the existing then replace the max , If it is less than the existing
then replace the min .
Implementation
function
stackWithMinMax
(){
let
items = [];
let
length =
this
.push = (
item
) => {
(length ===
){
110
else
const
data =
this
.peek();
let
the max
the min
this
.pop =
()
=> {
return
items[--length];
this
.peek =
()
=> {
return
items[length -
];
this
.max =
()
=> {
return
items[length -
].max;
this
.min =
()
=> {
return
items[length -
].min;
this
.size =
()
=> {
return
length;
111
this
.isEmpty =
()
=> {
return
length ===
this
.clear =
()
=> {
length =
items = [];
Test Case
Input:
let
SM =
new
stackWithMinMax
();
SM.push(
);
SM.push(
);
SM.push(
11
);
SM.push(
23
);
SM.push(
77
);
SM.push(
3
);
SM.push(
);
SM.pop();
console
.log(
`max: ${SM.max()}`
`min: ${SM.min()}`
);
Output:
"max: 77"
"min: 3"
112
Problem Statement -
Create a data structure called twoStacks which will be using only a single
array to store the data but will act as two different stacks .
● pop1() : This will remove the data from the first stack.
● pop2() : This will remove the data from the second stack.
Example
Input:
let
stack =
new
twoStacks(
10
);
stack.push1(
'stack1'
);
stack.push2(
'stack2'
);
//Pop data from first stack
console
.log(stack.pop1());
console
.log(stack.pop2());
Output:
"stack1"
"stack2"
113
Method 1: By dividing the array in two equal halves One of the approaches
to implementing two stacks in an array is by
dividing the array in two equal halves and using these halves as two different
stacks to store the data.
suppose we have two stacks with 4 and 6 elements and our array is of 10
length. No if we divide our array in two equal halves then it is going to have
two stacks of length 5 . If we push only 4 items in the first stack then it has
one space vacant and when we try to push 6 items in the second stack it will
overflow because it only has a capacity of 5 . We could have used the 1
vacant space of the first stack to store the data.
The concept we use here is we store the data on the two different ends in the
array (from start and from end ).
The first stack stores the data from the front that is at the index 0 and the
second stack stores the data from the end that is the index size-1.
Both stack push and pop data from opposite ends and to prevent the
Implementation
class
twoStacks
constructor
(n){
this
.size = n;
this
.top1 =
-1
this
.top2 = n;
this
.arr = [];
//Push in stack1
push1 = (
elm
) => {
if
this
.top1 <
this
.top2 -
){
this
.arr[++
this
.top1] = elm;
else
console
.log(
'Stack overflow'
);
return
false
//Push in stack2
push2 = (
elm
) => {
//Check if there is space in array
if
this
.top1 <
this
.top2 -
){
this
.arr[--
this
.top2] = elm;
else
console
.log(
'Stack overflow'
);
return
false
pop1 =
()
=> {
if
this
.top1 >=
){
let
elm =
this
.arr[
this
.top1];
this
.top1--;
return
elm;
else
console
.log(
'stack underflow'
);
return
false
}
© JavaScript Interview Guide | learnersbucket.com
115
pop2 =
()
=> {
if
this
.top2 <
this
.size){
let
elm =
this
.arr[
this
.top2];
this
.top2++;
return
elm;
else
console
.log(
'stack underflow'
);
return
false
Test Case
Input:
let
stack =
new
twoStacks(
10
);
stack.push1(
'stack1'
);
stack.push2(
'stack2'
);
console
.log(stack.pop1());
console
.log(stack.pop2());
Output:
"stack1"
"stack2"
116
as well, there was a need for some different models of original queue
A priority queue is one of the variants of the original queue. These elements
are added and removed based on their priorities. It is an
abstract data type that captures the idea of a container whose elements have
priorities attached to them. An element of highest priority always appears at
the front of the queue. If that element is removed, the next highest priority
element advances to the front.
hospitals, the one with at most priority are treated first and then the others.
It is used when we have to choose between the same values who have
● Prim’s algorithm : to store keys of nodes and extract minimum key nodes
at every step.
117
their priorities.
We will be using the first approach as we just have to place the
normally.
Implementation
function
PriorityQueue
(){
let
items = [];
//Container
function
QueueElement
element, priority
){
this
.element = element;
this
.priority = priority;
}
//Add a new element in queue
this
.enqueue =
function
element, priority
){
118
let
queueElement =
new
QueueElement(element, priority);
let
added =
false
for
(
let
i=
it at that place
if
items.splice(i,
, queueElement);
added =
true
break
}
//If element is not added
if
(!added){
items.push(queueElement);
this
.dequeue =
()
=> {
return
items.shift();
this
.front =
()
=> {
return
items[
];
this
.rear =
()
=> {
return
items[items.length -
];
this
.isEmpty =
()
=> {
return
items.length ==
119
this
.size =
()
=> {
return
items.length;
this
.print =
function
(){
for
let
i=
console
.log(
`${items[i].element} - ${items[i].priority}`
);
Test Case
Input:
let
pQ =
new
PriorityQueue();
pQ.enqueue(
1
);
pQ.enqueue(
);
pQ.enqueue(
);
pQ.enqueue(
11
);
pQ.enqueue(
13
);
pQ.enqueue(
10
);
pQ.dequeue();
pQ.print();
Output:
"10 - 3"
"5 - 2"
"6 - 1"
"11 - 1"
"13 - 1"
120
Implement LRU cache
LRU cache, briefly known as Least Recently Used cache is a caching policy
that is used to evict elements from the cache to make room for new elements
when the cache is full. It removes the least recently used elements first
before adding the new one.
In order to add a new element to this (say 5), we will have to first remove the
least recently used which in this case is 1.
121
Now when you try to access the 2 again it will be shifted to the end again as
it will be the most recently used one and 3 will become the least recently
used.
122
Following is the list of operations that will be formed on the LRU
cache.
● get(key) : Returns the cache value for the given item / page number.
● use(key) : Uses one of the existing values and re-arranges the cache by
marking the used one as most recently one.
Base Function
class
Node
constructor
(key, val) {
this
.key = key;
this
.val = val;
this
.prev =
null
this
.next =
null
const
LRUCache =
function
cap
){
this
.cap = cap;
this
.count =
;
this
.head =
null
this
.tail =
null
this
.cache =
new
Map
();
Use(Key)
● First get the queue node of the key from the hashmap.
● Then rearrange the doubly linked list to push the current node at the end or
tail marking it as most recently used.
//Uses the cache with given key and marks it as most recently used
this
.use =
function
key
){
const
node =
this
.cache.get(key);
if
(node ===
this
.head) {
return
else
if
(node ===
this
.tail) {
node.prev.next =
null
this
.tail = node.prev;
node.prev =
null
node.next =
this
.head;
this
.head.prev = node;
this
.head = node;
else
{
if
(node.prev) {
node.prev.next = node.next;
if
(node.next) {
node.next.prev = node.prev;
node.next =
this
.head;
node.prev =
null
this
.head.prev = node;
this
.head = node;
}
};
Evict
● First get the queue node from the hashmap and then remove this
this
.evict =
function
() {
const
keyToEvict =
this
.tail ?
this
.tail.key
null
if
(!
this
.tail) {
return
else
if
this
.head ===
this
.tail) {
124
this
.head =
null
this
.tail =
null
else
this
.tail.prev.next =
null
this
.tail =
this
.tail.prev;
if
(keyToEvict) {
this
.count--;
this
.cache.delete(keyToEvict);
};
Insert(key, val)
Adds a new element at the appropriate position in the queue and also inserts
it in the hashmap.
this
.insert =
function
key, val
){
const
node =
new
Node(key, val);
this
.count++;
this
.cache.set(key, node);
if
(!
this
.head) {
this
.head = node;
this
.tail = node;
else
this
.head.prev = node;
node.next =
this
.head;
this
.head = node;
};
Put(key, val)
● If the key already exists then use it and mark it as the most recent one.
● If the capacity is exceeded then remove the least recent one and then insert
the new key.
125
this
.put =
function
key, val
){
if
this
.cache.has(key)) {
const
node =
this
.cache.get(key);
node.val = val;
this
.use(key);
this
.cache.set(key, node);
else
if
this
.count >=
this
.cap) {
this
.evict();
this
.insert(key, val);
this
.use(key);
};
Get(key)
this
.get =
function
key
){
if
(!
this
.cache.has(key)) {
return
-1
;
}
const
node =
this
.cache.get(key);
this
.use(key);
return
node.val;
};
Display()
Prints all the items of the queue in the least to most order along with its
value.
this
.display =
function
(){
let
current =
this
.head;
while
(current){
126
console
.log(current.key, current.val);
current = current.next;
class
Node
constructor
(key, val) {
this
.key = key;
this
.val = val;
this
.prev =
null
this
.next =
null
const
LRUCache =
function
cap
){
this
.cap = cap;
this
.count =
0
this
.head =
null
this
.tail =
null
this
.cache =
new
Map
();
this
.get =
function
(
key
){
if
(!
this
.cache.has(key)) {
return
-1
const
node =
this
.cache.get(key);
this
.use(key);
return
node.val;
};
.put =
function
key, val
){
if
this
.cache.has(key)) {
const
node =
this
.cache.get(key);
node.val = val;
this
.use(key);
this
.cache.set(key, node);
}
else
127
if
this
.count >=
this
.cap) {
this
.evict();
this
.insert(key, val);
this
.use(key);
};
//Uses the cache with given key and marks it as
this
.use =
function
key
){
const
node =
this
.cache.get(key);
if
(node ===
this
.head) {
return
else
if
(node ===
this
.tail) {
node.prev.next =
null
this
.tail = node.prev;
node.prev =
null
node.next =
this
.head;
this
.head.prev = node;
this
.head = node;
}
else
if
(node.prev) {
node.prev.next = node.next;
if
(node.next) {
node.next.prev = node.prev;
node.next =
this
.head;
node.prev =
null
this
.head.prev = node;
this
.head = node;
}
};
this
.evict =
function
() {
const
keyToEvict =
this
.tail ?
this
.tail.key
null
if
(!
this
.tail) {
© JavaScript Interview Guide | learnersbucket.com
128
return
else
if
this
.head ===
this
.tail) {
this
.head =
null
this
.tail =
null
;
}
else
this
.tail.prev.next =
null
this
.tail =
this
.tail.prev;
if
(keyToEvict) {
this
.count--;
this
.cache.delete(keyToEvict);
};
//Helper function to add new cache in the queue
this
.insert =
function
key, val
){
const
node =
new
Node(key, val);
this
.count++;
this
.cache.set(key, node);
if
(!
this
.head) {
this
.head = node;
this
.tail = node;
else
this
.head.prev = node;
node.next =
this
.head;
this
.head = node;
};
this
.display =
function
(){
let
current =
this
.head;
while
(current){
console
.log(current.key, current.val);
current = current.next;
};
129
Test Case
Input:
const
lru =
new
LRUCache(
4
);
lru.put(
'a'
);
lru.put(
'b'
);
lru.put(
'c'
);
lru.put(
,
'd'
);
lru.display();
lru.use(
);
lru.display();
Output:
//LRU
"d"
"c"
"b"
"a"
//After using 2
"b"
4
"d"
"c"
"a"
130
site, we don’t want to trigger a search function and make a request to the
server as the user keeps typing each letter. We want the user to finish typing
and then wait for a specified window of time to see if the user is not going to
type anything else or has finished typing then
Implementation
const
debounce = (
func, delay
) => {
let
inDebounce;
return
function
() {
function
const
context =
this
const
args =
arguments
;
// base case
to it.
to reset
clearTimeout(inDebounce);
with apply
inDebounce = setTimeout(
()
=> func.apply(context,
args), delay);
131
};
};
Explanation
We created a function that will return a function. The outer function uses a
variable to keep track of timerId for the execution of the inner function.
The inner function will be called only after a specified window of time, to
achieve this we use setTimeout function.
If we are invoking for the first time, our function will execute at the end of
our delay. If we invoke and then reinvoke again before the end of our delay,
the delay restarts.
Test Case
Input:
const
onMouseMove = (
) => {
console
.clear();
console
.log(e.x, e.y);
const
debouncedMouseMove = debounce(onMouseMove,
50
);
window
.addEventListener(
'mousemove'
, debouncedMouseMove);
Output:
300 400
132
Immediate Flag
without any further delay. After initial execution it won’t run again till the
delay.
This flag will be optional which means if it is set to false then the debounce
function will behave normally.
Implementation
const
debounce = (
) => {
let
timeout;
return
function
() {
function
let
context =
this
,
args =
arguments
is true
is: Yes
const
// base case
to it.
to reset
clearTimeout(timeout);
133
timeout = setTimeout(
function
() {
variable
in 'immediate' mode
timeout =
null
immediate flag
if
(!immediate) {
func.apply(context, args);
}, wait);
function immediately
if
Explanation
We use a private variable to store the timerId, and return a closure from it
which will execute the callback function after debouncing.
Inside the closure, store the context of the current function and its arguments
and pass it later to the callback function while executing it with the help of
apply method.
Then assign the timer and execute the function only when the
134
Test Case
Input:
const
onMouseMove = (
e
) => {
console
.clear();
console
.log(e.x, e.y);
const
debouncedMouseMove = debounce(onMouseMove,
50
);
window
.addEventListener(
'mousemove'
, debouncedMouseMove);
Output:
314 419
135
Implement throttling function
correctly.
If the user spam’s the click then this will make an API call on each click.
This is not what we want, we want to restrict the number of API calls that
can be made. The other call will be made only after a
Throttling helps us to gain control over the rate at which function is called or
executes.
Implementation
const
throttle = (
func, limit
) => {
lastFunc;
let
lastRan;
return
function
() {
const
context =
this
const
args =
arguments
136
if
(!lastRan) {
func.apply(context, args);
lastRan =
Date
.now();
else
clearTimeout(lastFunc);
// start it again
lastFunc = setTimeout(
function
() {
if
((
Date
func.apply(context, args);
lastRan =
Date
.now();
}, limit - (
Date
.now() - lastRan));
Test Case
Input:
const
print =
()
=> {
console
.log(
"hello"
);
}
const
throttled = throttle(print,
2500
);
window
.addEventListener(
'mousemove'
, throttled,
false
);
Output:
137
For example, Call a search function to fetch the result when the
user has stopped typing in the search box. If the user keeps on
typing then reset the function.
server on the user’s click. If the user spam’s the click then also
there will be specific calls only. Like, make each call after 10
seconds.
138
Definition
According to MDN –
From the definition, we can derive that to create a custom instanceof method
we will have to check if the prototype property of a constructor appears
anywhere in the prototype chain of an object .
Thus we will have to check first, if the provided input is an object or not and
later keep checking if the prototype property appears
getPrototypeOf(object) method.
instanceOf = (
obj, target
) => {
false
if
(obj ===
null
||
typeof
obj !==
'object'
return
false
while
(obj){
if
return
true
obj = obj.__proto__;
return
false
139
Test Case
Input:
class
{}
class
Q
extends
{}
const
q=
new
Q()
console
.log(instanceOf(q, Q))
// true
console
.log(instanceOf(q, P))
// true
console
.log(instanceOf(q,
Object
))
// true
function
R
() {}
console
.log(instanceOf(q, R))
// false
R.prototype = Q.prototype
console
.log(instanceOf(q, R))
// true
R.prototype = {}
console
.log(instanceOf(q, R))
// false
const
instanceOf = (
obj, target
) => {
false
if
(obj ==
null
||
typeof
obj !==
'object'
return
false
const
proto =
Object
.getPrototypeOf(obj);
target's prototype
return
true
: instanceOf(proto,
target);
Test Case
Input:
class
{}
class
extends
{}
const
q=
new
Q()
console
.log(instanceOf(q, Q))
// true
console
.log(instanceOf(q, P))
// true
console
.log(instanceOf(q,
Object
))
// true
function
() {}
140
console
.log(instanceOf(q, R))
// false
R.prototype = Q.prototype
console
.log(instanceOf(q, R))
// true
R.prototype = {}
console
.log(instanceOf(q, R))
// false
141
There are multiple ways to invoke functions in JavaScript and one of the
ways is with the “new” keyword calling function as a constructor.
The simplest way is by using the instanceof method, but it really does not
cover all the edge cases.
function
() {
if
((
this
instanceof
arguments
.callee) ) {
console
.log(
"OK, new"
);
else
console
.log(
"OK, function"
);
Test Case
Input:
var
Z=
new
A();
Z.lolol = A;
Z.lolol();
Output:
// OK, new
To solve this we will need some additional checks along with the
instanceOf method.
142
Using one extra flag to determine if the function is constructed or not does
the work.
function
() {
here,
if
this
instanceof
A && !
this
._constructed )
this
._constructed =
true
console
.log(
'OK, new'
);
else
console
.log(
'OK, function'
);
}
Test Case
Input:
A();
// Ok, function
new
A();
// OK, new
new
A();
// Ok, new
A(
);
// OK, function
var
X=
new
A(
4
);
// OK, new
var
Z=
new
A();
// OK, new
Z.lolol = A;
Z.lolol();
// OK, function
var
Y = A;
Y();
// OK, function
var
y=
new
Y();
// OK, new
y.lolol = Y;
y.lolol();
// OK, function
Output:
"OK, function"
"OK, new"
"OK, new"
"OK, function"
"OK, new"
"OK, new"
"OK, function"
143
"OK, function"
"OK, new"
"OK, function"
As per MDN –
new.target is undefined.
function
){
if
new
.target ) {
console
.log(
'OK, new'
);
else
console
.log(
'OK, function'
);
Test Case
Input:
A();
// Ok, function
new
A();
// OK, new
new
A();
// Ok, new
A(
);
// OK, function
var
X=
new
A(
);
// OK, new
var
Z=
new
A();
// OK, new
Z.lolol = A;
Z.lolol();
// OK, function
var
Y = A;
Y();
// OK, function
144
var
y=
new
Y();
// OK, new
y.lolol = Y;
y.lolol();
// OK, function
Output:
"OK, function"
"OK, new"
"OK, new"
"OK, function"
"OK, new"
"OK, new"
"OK, function"
"OK, function"
"OK, new"
"OK, function"
Problem Statement -
Create a simple store class (hashSet) with set(key, value) , get(key) , &
has(key) methods.
Example
const
store =
new
Store();
store.set(
'a'
10
);
store.set(
'b'
20
);
store.set(
'c'
30
);
store.get(
'b'
);
// 20
store.has(
'c'
);
// true
We can do this by simply creating a function with an object that will store
the key-value and these methods.
const
Store =
function
(){
this
.list = {};
this
.set =
function
key, value
){
this
.list[key] = value;
this
.get =
function
key
){
146
return
this
.list[key];
this
.has =
function
key
){
return
!!
this
.list[key];
}
Test Case
Input:
const
store =
new
Store();
store.set(
'a'
10
);
store.set(
'b'
20
);
store.set(
'c'
30
);
console
.log(store.get(
'b'
));
console
.log(store.has(
'c'
));
console
.log(store.get(
'd'
));
console
.log(store.has(
'e'
));
Output:
20
true
undefined
false
147
Problem Statement -
Create a toggle function that accepts a list of arguments and toggles each of
them when invoked in a cycle.
Example
let
hello = toggle(
"hello"
);
hello()
// "hello";
hello()
// "hello";
let
onOff = toggle(
"on"
,
"off"
);
onOff()
// "on"
onOff()
// "off"
onOff()
// "on"
The toggle function returns each value clockwise on each call of the function
and the same can be done by returning a function from the
toggle function forming a closure over the values to track the cycle.
Implementation
const
toggle = (
...list
) => {
let
current =
-1
;
const
length = list.length;
return
function
(){
> length
current = (current +
) % length;
return
list[current];
Test Case
const
hello = toggle(
"1"
"2"
);
console
.log(hello());
// "1"
console
.log(hello());
// "2"
console
.log(hello());
// "1"
148
Sampling function
Problem Statement -
executes that input function once for a given count of calls. Known as a
sampling function.
Example
function
message
(){
console
.log(
"hello"
);
const
sample = sampler(message,
);
sample();
sample();
sample();
sample(); // “hello”
sample();
sample();
sample();
sample();
number of calls.
To create a sampling function we can create a closure that will track how
many times the function has been called and once it reaches the
Implementation
function
sampler
){
let
counter =
return
function
...args
){
let
lastArgs = args;
context =
this
?? context;
to the counts
if
return
fn.apply(context, args);
counter =
};
Test Case
function
message
(){
console
.log(
"hello"
);
const
sample = sampler(message,
);
sample();
sample();
sample();
sample();
// hello
sample();
sample();
sample();
sample();
// hello
150
can halt the function execution for the given amount of time.
● C or PHP : sleep(2)
● JAVA : Thread.sleep(2000)
● Python : time.sleep(2)
● Go : time.Sleep(2 * time.Second)
Javascript does not have any inbuilt function for this, But thanks to the
introduction of promises and async-await in ES2018, we can now
Implementation
const
sleep = (
milliseconds
) => {
return
new
Promise
resolve
=> setTimeout(resolve,
milliseconds))
};
This will create a wrapper function which will resolve the promise
We can use this to prevent function execution for a certain amount of time.
sleep(
500
).then(
()
=> {
//do stuff
console
.log(
);
});
const
performAction =
async
() => {
await
sleep(
2000
);
//do stuff
151
performAction();
This works well, however due to how javascript works this does not stop the
entire program execution like it does in other programming
152
Given an object with a cycle, remove the cycle or circular reference from it.
Example
Input:
const
List =
function
val
){
this
.next =
null
this
.val = val;
};
const
item1 =
new
List(
10
);
const
item2 =
new
List(
20
);
const
item3 =
new
List(
30
);
item1.next = item2;
item2.next = item3;
item3.next = item1;
// this form a cycle, if you console.log this you will see a circular object,
// like, item1 -> item2 -> item3 -> item1 -> so on.
Output:
// removes cycle
If you see the above example, we have created a list object, that accepts a
value and pointer to the next item in the list, similar to a linked list , and
using this we have created the circular object.
We have to create a function that will break this cycle, in this example to
break the cycle we will have to delete the next pointer of the item3 .
153
There are two places where this cycle removal can take place.
Normal use
We can use WeakSet which is used to store only unique object references
and detect if the given object was previously detected or not, if it was
detected then delete it.
const
removeCycle = (
obj
) => {
//set store
const
set =
new
WeakSet
([obj]);
function
iterateObj
obj
){
for
let
key
in
obj) {
chain
if
(obj.hasOwnProperty(key)) {
if
typeof
obj[key] ===
'object'
){
// then delete it
if
(set.has(obj[key])){
delete
obj[key];
else
set.add(obj[key]);
iterateObj(obj[key]);
})(obj);
154
Test Case
Input:
const
List =
function
val
){
this
.next =
null
this
.val = val;
};
const
item1 =
new
List(
10
);
const
item2 =
new
List(
20
);
const
item3 =
new
List(
30
);
item1.next = item2;
item2.next = item3;
item3.next = item1;
removeCycle(item1);
console
.log(item1);
Output:
/*
*/
JSON.stringify accepts a replacer function that can be used to alter the value
of the stringification process.
We can use the same function to detect and remove the cycle from the
object.
const
getCircularReplacer =
()
=> {
const
seen =
new
WeakSet
();
return
key, value
) => {
if
typeof
value ===
'object'
null
){
if
(seen.has(value)) {
155
return
seen.add(value);
return
value;
};
};
Test Case
Input:
const
List =
function
(
val
){
this
.next =
null
this
.val = val;
};
const
item1 =
new
List(
10
);
const
item2 =
new
List(
20
);
const
item3 =
new
List(
30
);
item1.next = item2;
item2.next = item3;
item3.next = item1;
console
.log(
JSON
.stringify(item1, getCircularReplacer()));
Output:
"{'next':{'next':{'val':30},'val':20},'val':10}"
156
Problem Statement -
Given multiple dimensional arrays, create a filter function that takes a
callback function as input and returns a new array with all elements that
have passed the test implemented in the callback function.
Example
Input:
const
arr = [[
,[
,[
'foo'
,{
'a'
'b'
:
2
}]],
'bar'
]];
const
filtered = filter(arr, (
) =>
typeof
e ===
'string'
);
console
.log(JSON.stringify(filtered));
Output:
[[[[
"foo"
]],
"bar"
]]'
To filter a multi-dimensional array, we will have to filter each
const
filter = (
arr, test
) => {
const
result = [];
for
let
of
arr) {
//if sub-array
if
Array
.isArray(a)) {
const
result.push(output);
else
if
(test(a)) {
result.push(a);
157
}
//return the result
return
result;
};
Test Case
Input:
const
arr = [[
,[
,[
"foo"
, { a:
, b:
}]],
"bar"
]];
const
typeof
e ===
"number"
);
console
.log(
JSON
.stringify(filtered));
Output:
[[
,[
,[
]]]]
We can extend this method and implement a multiFilter on the array’s
prototype itself that will take a callback function and filter the array based on
the callback’s result.
Array
.prototype.multiFilter =
function
OceanofPDF.com
(
test
){
//original array;
const
originalArray =
this
const
filter = (
arr, test
) => {
const
result = [];
for
let
a
of
arr) {
//if sub-array
if
Array
.isArray(a)) {
const
result.push(output);
158
else
if
(test(a)) {
result.push(a);
return
result;
};
return
filter(originalArray, test);
};
Test Case
Input:
const
arr = [[
1
,[
,[
"foo"
, { a:
, b:
}]],
"bar"
]];
const
filtered = arr.multiFilter((
) =>
typeof
e ===
"number"
);
console
.log(
JSON
.stringify(filtered));
Output:
[[
,[
,[
]]]]
159
Problem Statement -
Given a nested array and a callback function, count all the elements that
pass the test in the callback and return the count.
Example
Input:
const
arr = [[
,[
,[
"foo"
, { a:
, b:
}]],
"bar"
]];
const
typeof
e ===
"number"
);
console
.log(count);
Output:
let
countInArray =
function
inputArr, test
){
//track the count
let
count =
const
search = (
arr, test
) => {
for
let
of
arr) {
if
(test(a)) {
count +=
160
//if sub-array
if
Array
.isArray(a)) {
search(a, test);
};
//search
search(inputArr, test);
//return
return
count;
};
Test Case
Input:
const
arr = [[
,[
,[
"foo"
, { a:
, b:
2
}]],
"bar"
]];
const
typeof
e ===
"number"
);
console
.log(count);
Output:
161
Problem Statement -
numbers.
Example
Input:
"#ff33ff"
Output:
"r"
255
"g"
51
"b"
255
– 15”.
RGB format is a combination of three colors, red, green, and blue in the
range of 0 – 255. A hex color code is the hexadecimal
representation of the RGB numbers.
There are multiple ways in which we can convert the HEXA color
162
We can use the slice() to get the two numbers and then use the parseInt()
method that accepts a radix value and converts the string to a number.
const
hex2rgb = (
hex
) => {
const
r=
parseInt
(hex.slice(
,
3
),
16
);
const
g=
parseInt
(hex.slice(
),
16
);
const
b=
parseInt
(hex.slice(
,
7
),
16
);
// return {r, g, b}
return
{ r, g, b };
console
.log(hex2rgb(
"#ff33ff"
));
/*
"r": 255,
"g": 51,
"b": 255
*/
In case we are given a short form of Hexa code like #f3f , we will have to
convert it to the original form.
//create full hex
const
fullHex = (
hex
) => {
let
r = hex.slice(
);
let
g = hex.slice(
);
let
b = hex.slice(
3
,
);
r=
parseInt
(r+r,
16
);
g=
parseInt
(g+g,
16
);
b=
parseInt
(b+b,
16
);
// return {r, g, b}
return
{ r, g, b };
163
const
hex2rgb = (
hex
) => {
if
(hex.length ===
){
return
fullHex(hex);
const
r=
parseInt
(hex.slice(
1
),
16
);
const
g=
parseInt
(hex.slice(
),
16
);
const
b=
parseInt
(hex.slice(
5
),
16
);
// return {r, g, b}
return
{ r, g, b };
console
.log(hex2rgb(
"#f3f"
));
/*
"r": 255,
"g": 51,
"b": 255
}
*/
The match() method of string accepts a regex and returns the array of
matching elements.
We can then use this array to parse the HEXA values to RGB.
const
hex2rgb = (
hex
) => {
const
rgbChar = [
'r'
'g'
'b'
];
const
normal = hex.match(
/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i
);
if
(normal) {
return
normal.slice(
).reduce((
a, e, i
) => {
a[rgbChar[i]] =
parseInt
(e,
16
);
return
a;
}, {});
164
const
shorthand = hex.match(
/^#([0-9a-f])([0-9a-f])([0-9a-f])$/i
);
if
(shorthand) {
return
shorthand.slice(
).reduce((
a, e, i
) =>
a[rgbChar[i]] =
0x11
parseInt
(e,
16
);
return
a;
}, {});
return
null
console
.log(hex2rgb(
"#ff33ff"
));
/*
"r": 255,
"g": 51,
"b": 255
*/
console
.log(hex2rgb(
"#f3f"
));
/*
"r": 255,
"g": 51,
"b": 255
*/
165
Problem Statement -
codes.
Example
Input:
255
51
,
255
Output:
"#ff33ff"
RGB format is a combination of three colors, red, green, and blue in the
range of 0 – 255. A hex color code is the hexadecimal
15”.
HEXA value, just pad the number with 0 if it is a single digit and
The toString() method accepts a radix value and converts the value to that.
const
componentToHex = (
) => {
const
hex = c.toString(
16
);
return
hex.length ==
"0"
+ hex : hex;
166
const
rgbToHex = (
r, g, b
) => {
return
"#"
+ componentToHex(r) + componentToHex(g)
+ componentToHex(b);
}
console
.log(rgbToHex(
255
51
255
));
//"#ff33ff"
rgbToHex = (
r, g, b
) => {
return
"#"
+ ((
<<
24
) + (r <<
16
) + (g <<
)+
b).toString(
16
).slice(
);
console
.log(rgbToHex(
255
51
255
));
//"#ff33ff"
rgbToHex = (
r, g, b
) =>
'#'
+ [r, g, b]
.map(
=> x.toString(
16
).padStart(
'0'
)).join(
''
console
.log(rgbToHex(
255
,
51
255
));
//"#ff33ff"
167
Problem Statement -
following functionalities.
● deleteFile(name) – Deletes the file with the given name at the current
path.
● getRootDirectory – Returns the root directory and all its nested childs.
directory.
Boilerplate
const
FileSystem =
function
(){
this
.directory = {
"root"
: {}};
this
.currentDir =
this
.directory[
"root"
];
this
.currentDirPath =
"root"
};
168
this
.createDirectory =
function
name
){
this
.currentDir[name] = {};
this
.changeDirectory =
function
path
){
this
.currentDir =
this
._changeDirectoryHelper(path);
this
.currentDirPath = path;
}
this
._changeDirectoryHelper =
function
path
){
const
paths = path.split(
"-"
);
let
current =
this
.directory;
for
let
key
of
paths){
current = current[key];
return
current;
169
this
.getCurDirectoryPath =
function
(){
return
this
.currentDirPath;
this
.getCurDirectory =
function
(){
return
this
.currentDir;
Add file
At the current path the files will be added to the key “files” as an array
making it easy to add and remove them.
this
.addFile =
function
fileName
){
if
this
.currentDir.files){
this
.currentDir.files.push(fileName);
}
else
this
.currentDir[
"files"
] = [fileName];
return
true
Remove file
To remove files simply filter the files array at the current directory.
this
.deleteFile =
function
fileName
){
this
.currentDir.files =
this
.currentDir.files.filter((
=> e !==
fileName);
return
true
To delete the directory, change to the current path and delete any of the sub-
directory.
170
this
.deleteDirectory =
function
(
name
){
delete
this
.currentDir[name];
this
.getRootDirectory =
function
(){
return
this
.directory;
Complete code
const
FileSystem =
function
(){
this
.directory = {
"root"
: {}};
this
.currentDir =
this
.directory[
"root"
];
this
.currentDirPath =
"root"
this
.createDirectory =
function
name
){
this
.currentDir[name] = {};
this
.changeDirectory =
function
path
){
this
.currentDir =
this
._changeDirectoryHelper(path);
this
.currentDirPath = path;
this
._changeDirectoryHelper =
function
(
path
){
const
paths = path.split(
"-"
);
let
current =
this
.directory;
for
let
key
of
paths){
current = current[key];
return
current;
this
.getCurDirectoryPath =
function
(){
return
this
.currentDirPath;
171
this
.getCurDirectory =
function
(){
return
this
.currentDir;
}
this
.addFile =
function
fileName
){
if
this
.currentDir.files){
this
.currentDir.files.push(fileName);
else
this
.currentDir[
"files"
] = [fileName];
}
return
true
this
.deleteFile =
function
fileName
){
this
.currentDir.files =
this
.currentDir.files.filter((
=> e !==
fileName);
return
true
;
this
.deleteDirectory =
function
name
){
delete
this
.currentDir[name];
this
.getRootDirectory =
function
(){
return
this
.directory;
}
}
Test Case
Input:
const
dir =
new
FileSystem();
dir.createDirectory(
'prashant'
);
dir.changeDirectory(
'root-prashant'
);
dir.addFile(
'index.html'
);
dir.addFile(
'app.js'
);
dir.changeDirectory(
'root'
);
dir.createDirectory(
'practice'
);
dir.changeDirectory(
'root-practice'
);
dir.addFile(
'index.html'
);
dir.addFile(
'app.js'
);
dir.createDirectory(
'build'
);
172
dir.changeDirectory(
'root-practice-build'
);
dir.addFile(
'a.png'
);
dir.addFile(
'b.jpg'
);
dir.deleteFile(
'a.png'
);
dir.changeDirectory(
'root'
);
dir.deleteDirectory(
'prashant'
);
console
.log(dir.getRootDirectory());
Output:
{
"root"
:{
"practice"
:{
"files"
:[
"index.html"
"app.js"
],
"build"
:{
"files"
:[
"b.jpg"
}
}
173
API
Problem Statement -
Example
Input:
const
z=
new
Stream();
z.subscribe((
value
) =>
console
.log(value));
z.subscribe((
value
) =>
console
.log(value *
));
z.subscribe((
value
) =>
console
.log(value *
));
z.push(
);
Output:
6
We have to create a basic implementation of a streams API.
Streams API passes the data in chunks which means as we keep pushing
values it should go through all the subscriptions.
whenever a value is pushed, call all the subscription methods with this
value.
174
Class-based implementation
In the constructor we will initialize the cache and then create two
subscribe will cache all the methods passed to it and in the push method we
will call all the subscription methods with the value
received as an argument.
class
Stream
constructor
(){
this
.subscriptions = [];
subscribe(method){
if
typeof
method !==
'function'
){
throw
new
Error
'Invalid method!.'
);
this
.subscriptions.push(method);
push(val){
this
.subscriptions.forEach((
method
) => {
method.call(
this
, val);
});
Test Case
Input:
const
z=
new
Stream();
z.subscribe((
value
) =>
console
.log(value));
z.subscribe((
value
) =>
console
.log(value *
));
z.subscribe((
value
) =>
console
.log(value *
));
z.push(
);
Output:
2
4
175
Function-based implementation
function
Stream
() {
let
subscriptions = [];
this
.subscribe = (
method
) => {
if
typeof
method !==
'function'
){
throw
new
Error
'Invalid method!.'
);
subscriptions.push(method);
this
.push = (
val
) => {
subscriptions.forEach((
method
) => {
method.call(
this
, val);
});
}
Test Case
Input:
const
z=
new
Stream();
z.subscribe((
value
) =>
console
.log(value));
z.subscribe((
value
) =>
console
.log(value *
));
z.subscribe((
value
) =>
console
.log(value *
));
z.push(
);
Output:
176
Problem Statement -
Create a function that memorizes or caches the result for the given
input so that the subsequent calls for the same inputs will be faster.
Example
slowFunc(params)
const
memoized = memoize(slowFunc);
memoized(params)
the result
memoized(params)
//all the subsequents call for the same input will be faster.
memoized(differentParams)
memoized(differentParams)
we cache the result for the given input and return it for subsequent call of
the same input rather than performing the expensive computation
again.
We will create a closure with the higher-order function that will cache the
result for the given input. If a call is made and the result for the input is
stored in the cache then we will return it, otherwise, execute the function
and cache its result.
const
memoize =
function
fn
){
const
cache = {};
return
function
(){
const
KEY =
JSON
.stringify(
arguments
);
it
177
if
(cache[KEY]) {
return
cache[KEY]
the result
const
evaluatedValue = fn(...arguments);
cache[KEY] = evaluatedValue;
return
evaluatedValue;
};
Test Case
Input:
function
factorial
){
if
(n ===
|| n ===
){
return
return
factorial(n
-1
) * n;
};
const
memoizedFactorial = memoize(factorial);
let
a = memoizedFactorial(
100
console
.log(a);
let
b = memoizedFactorial(
100
console
.log(b);
Output:
9.33262154439441e+157
// slow
9.33262154439441e+157
// faster
178
Problem Statement -
that performs the basic actions like add, subtract, divide, and multiply.
Example
calculator.add(
10
).subtract(
).divide(
).multiply(
);
console
.log(calculator.total);
//20
Method chaining is an object-oriented paradigm, in which the
chaining.
1. Using objects.
2. With functions.
Methods inside the object can refer to the current object using this keyword,
thus we can use the same to perform our operations and
const
calculator = {
total:
add:
function
val
){
this
.total += val;
return
this
},
subtract:
function
val
){
this
.total -= val;
return
this
179
},
divide:
function
val
){
this
.total /= val;
return
this
},
multiply:
function
val
){
this
.total *= val;
return
this
;
}
};
Test Case
Input:
calculator.add(
10
).subtract(
).divide(
).multiply(
);
console
.log(calculator.total);
Output:
20
const
CALC =
function
(){
this
.total =
this
.add = (
val
) => {
this
.total += val;
return
this
this
.subtract = (
val
) => {
this
.total -= val;
return
this
180
this
.multiply = (
val
) => {
this
.total *= val;
return
this
;
}
this
.divide = (
val
) => {
this
.total /= val;
return
this
this
.value =
()
=>
this
.total;
Test Case
Input:
const
calculator =
new
CALC();
calculator.add(
10
).subtract(
).divide(
).multiply(
);
console
.log(calculator.total);
Output:
20
181
Example
Input:
computeAmount().lacs(
15
).crore(
).crore(
).lacs(
20
).thousand(
45
).crore(
).v
alue();
Output:
143545000
implementations.
const
ComputeAmount =
function
(){
this
.store =
this
.crore =
function
val
){
this
.store += val *
Math
.pow(
10
);
return
this
};
this
.lacs =
function
val
){
this
.store += val *
Math
.pow(
10
);
return
this
182
this
.thousand =
function
val
){
this
.store += val *
Math
.pow(
10
);
return
this
this
.hundred =
function
val
){
this
.store += val *
Math
.pow(
10
);
return
this
this
.ten =
function
val
){
this
.store += val *
10
return
this
this
.unit =
function
val
){
this
.store += val;
return
this
this
.value =
function
(){
return
this
.store;
Test Case
Input:
const
computeAmount =
new
ComputeAmount();
const
amount =
computeAmount.lacs(
15
).crore(
).crore(
).lacs(
20
).thousand(
45
).crore(
).val
ue();
console
.log(amount ===
143545000
);
Output:
true
183
We will form closure and return a new object from it that will have all the
logic encapsulated. This way we won't have to create a constructor
everytime and the data won’t duplicate too.
const
ComputeAmount =
function
(){
return
store:
crore:
function
val
){
this
.store += val *
Math
.pow(
10
);
return
this
},
lacs:
function
val
){
this
.store += val *
Math
.pow(
10
);
return
this
},
thousand:
function
val
){
this
.store += val *
Math
.pow(
10
);
return
this
},
hundred:
function
(
val
){
this
.store += val *
Math
.pow(
10
);
return
this
},
ten:
function
val
){
this
.store += val *
10
return
this
},
unit:
function
val
){
this
.store += val;
return
this
},
184
value:
function
(){
return
this
.store;
Test Case
Input:
const
amount =
ComputeAmount().lacs(
).lacs(
).thousand(
10
).ten(
1
).unit(
).value();
console
.log(amount ===
1010011
);
const
amount2 =
ComputeAmount().lacs(
15
).crore(
).crore(
).lacs(
20
).thousand(
45
).crore(
).v
alue();
console
.log(amount2 ===
143545000
);
Output:
true
true
185
Implement clearAllTimeout
Problem Statement -
setTimeout at once.
Example
Input:
setTimeout(
()
=> {
console
.log(
"hello"
)},
2000
);
setTimeout(
()
=> {
console
.log(
"hello1"
)},
3000
);
setTimeout(
()
=> {
console
.log(
"hello2"
)},
4000
);
setTimeout(
()
=> {
console
.log(
"hello3"
)},
5000
);
clearAllTimeout();
setTimeout(
()
=> {
console
.log(
"hello4"
)},
5000
);
Output:
"hello4"
have to do is to clear all the active timers and the same can be done by
clearing all timeoutIds using clearTimeout .
window
.clearAllTimeout =
function
(){
while
(timeoutIds.length){
clearTimeout(timeoutIds.pop());
186
But to clear all the timeoutIds at once, we will need to store them
somewhere, let’s say in an array. For which we will override the
window
.timeoutIds = [];
const
originalTimeoutFn =
window
.setTimeout;
window
.setTimeout =
function
(
fn, delay
){
const
id = originalTimeoutFn(fn, delay);
timeoutIds.push(id);
return
id;
Complete code
window
.timeoutIds = [];
const
originalTimeoutFn =
window
.setTimeout;
window
.setTimeout =
function
fn, delay
){
const
id = originalTimeoutFn(fn, delay);
timeoutIds.push(id);
return
id;
window
.clearAllTimeout =
function
(){
while
(timeoutIds.length){
clearTimeout(timeoutIds.pop());
}
}
187
Test Case
setTimeout(
()
=> {
console
.log(
"hello"
)},
2000
);
setTimeout(
()
=> {
console
.log(
"hello1"
)},
3000
);
setTimeout(
()
=> {
console
.log(
"hello2"
)},
4000
);
setTimeout(
()
=> {
console
.log(
"hello3"
)},
5000
);
clearAllTimeout();
If we test this, this runs as expected. It will clear all the timeouts, as
setTimeout is an Asynchronous function, meaning that the timer
function will not pause execution of other functions in the functions stack,
thus clearAllTimeout runs and cancels them before they can be executed.
overridden.
One thing we could do over here is to wrap these inside a closure or higher-
order function or an Object to keep it restricted.
This way we won’t be interfering with existing methods and can still get
our work done.
const
MY_TIMER = {
timeoutIds : [],
setTimeout :
function
fn,delay
){
let
id = setTimeout(fn,delay);
this
.timeoutIds.push(id);
return
id;
},
188
//MY_TIMER's clearAllTimeout
clearAllTimeout :
function
(){
while
this
.timeoutIds.length){
clearTimeout(
this
.timeoutIds.pop());
}
};
Test Case
Input:
const
id = MY_TIMER.setTimeout(
()
=> {
console
.log(
"hello"
)},
1000
);
console
.log(id);
MY_TIMER.clearAllTimeout();
Output:
13
//timeoutId
189
Implement ClearAllInterval
Problem Statement -
Example
Input:
setInterval(
()
=> {
console
.log(
"Hello"
);
},
2000
);
setInterval(
()
=> {
console
.log(
"Hello2"
);
},
500
);
setInterval(
()
=> {
console
.log(
"Hello3"
);
},
1000
Output:
"Hello3"
First, to clear all the intervals at once, we need to store all of their ids so
that they can be cleared one by one using the clearInterval method.
190
Thus we can define a global variable intervalIds that will store the ids of the
setIntervals and override the existing setInterval function to push the ids in
this global variable.
window
.intervalIds = [];
const
originalIntervalFn =
window
.setInterval;
//overriding the original
window
.setInterval =
function
fn, delay
){
const
id = originalIntervalFn(fn, delay);
intervalIds.push(id);
return
id;
window
.clearAllInterval =
function
(){
while
(intervalIds.length){
clearInterval(intervalIds.pop());
Test Case
Input:
setInterval(
()
=> {
console
.log(
"Hello"
);
},
2000
);
setInterval(
()
=> {
console
.log(
"Hello2"
);
},
500
);
clearAllInterval();
setInterval(
()
=> {
console
.log(
"Hello3"
);
191
},
1000
Output:
"Hello3"
overridden.
One thing you could do over here is to wrap these inside a closure or
higher-order function or an Object to keep it restricted.
This way we won’t be interfering with existing methods and can still get
our work done.
const
MY_TIMER = {
intervalIds : [],
setInterval :
function
fn,delay
){
let
id = setInterval(fn,delay);
this
.intervalIds.push(id);
return
id;
},
//MY_TIMER's clearAllTimeout
clearAllInterval :
function
(){
while
this
.intervalIds.length){
clearTimeout(
this
.intervalIds.pop());
};
Test Case
Input:
MY_TIMER.setInterval(
()
=> {
console
.log(
"Hello"
);
},
2000
);
192
MY_TIMER.setInterval(
()
=> {
console
.log(
"Hello2"
);
},
500
);
MY_TIMER.clearAllInterval();
MY_TIMER.setInterval(
()
=> {
console
.log(
"Hello3"
);
},
1000
);
Output:
"Hello3"
193
Example
MY_TIMER.setTimeout(
()
=> {
console
.log(
},
2500
);
MY_TIMER.setTimeout(
()
=> {
console
.log(
2
)
},
2000
);
MY_TIMER.run();
Output:
Create a custom object that will have setTimeout , clearTimeout , and run
method.
In the setTimeout , store each entry in the queue, for the delay, add the input
to the current time to determine when it should be invoked. Also after each
entry, sort the queue in ascending order based on the time.
Using the timer id, we can remove the entry from the queue in
clearTimeout .
In the run method, we will run an infinite while loop, in each iteration, we
will get the first element from the queue (as it will be with the least time),
check if its time is past the current time then invoke it, else push it back into
the queue.
Do it for all the entries in the queue. Add a condition to check if there are
no more timers (the queue is empty) to run then break the loop.
const
MY_TIMER = {
timerId:
queue: [],
setTimeout:
function
){
const
id =
this
.timerId++;
this
.queue.push({
id,
cb,
time:
Date
.now() + time,
args,
});
this
.queue.sort((
a, b
// return the id
return
id;
},
clearTimeout:
function
removeId
){
this
.queue =
this
.queue.filter((
{ id }
) => id
!== removeId);
},
run:
function
() {
while
true
){
const
entry =
this
.queue.shift();
const
// invoke it
if
(
Date
cb(...args);
else
this
.queue.unshift(entry);
if
this
.queue.length ===
){
break
;
}
};
Test Case
Input:
MY_TIMER.setTimeout(
()
=> {
console
.log(
},
2500
);
MY_TIMER.setTimeout(
()
=> {
console
.log(
},
2000
);
MY_TIMER.setTimeout(
()
=> {
console
.log(
},
2500
);
196
MY_TIMER.setTimeout(
()
=> {
console
.log(
},
3000
);
MY_TIMER.run();
Output:
197
Currying - Problem 1
What is currying?
invoked which accepts the next argument inline. With the help of
Example
//normal function
sum(
//should return 6
//currying function
sum(
)(
2
)(
//should return 6
If you notice in the currying function for each function call sum(1) we are
returning a function which accepts the next argument sum(1)(2)
There is no specified limit to how many times you return a function, also
there are different variations to currying like the first function will accept
the 2 arguments and the next function can accept any number
//variations of currying
sum(
)(
)(
sum(
1
,
)(
198
sum(
)(
sum(
3
)
//6
Now you may be wondering that each function call returns a new
function then how the value is returned from it?. Well for that we have to
decide a base condition that should return the output.
For example, if no argument is passed in the next function call then return
the value or if we have reached 5 arguments then return the
value, etc.
sum(
)(
)(
)()
sum(
)(
3
)()
sum(
)(
)()
sum(
)()
//OR
//when we reach 5 arguments then return the value rather than new function
sum(
1
,
sum(
)(
)
sum(
)(
sum(
)(
,
5
sum(
)(
)(
)(
)(
sum(
,
4
)(
Problem Statement -
199
Example
sum(
)
sum(
)(
)(
)(
sum(
)(
sum(
1
,
)(
sum(
)(
sum = (
...args
) => {
const
storage = [...args];
//base case
if
(storage.length ===
){
return
storage.reduce((
a, b
) => a + b,
);
}
...args is the rest operator which aggregates all the passed arguments as an
array.
The purpose of using the variable is that, when the arguments passed is less
than the limit then we will use this further to store the next argument in the
closure.
200
const
sum = (
...args
) => {
const
storage = [...args];
//base case
(storage.length ===
){
return
storage.reduce((
a, b
) => a + b,
);
else
const
temp =
function
...args2
){
storage.push(...args2);
if
(storage.length ===
){
return
storage.reduce((
a, b
) => a + b,
);
else
{
return
temp;
return
temp;
201
We merge the arguments of inner function in the existing storage and check
if we have reached the limit or not in each call.
If we have reached the limit return the sum of them otherwise return the
same function again.
Test Case
Input:
const
res = sum(
,
2
);
const
res2 = sum(
)(
)(
)(
);
const
res3 = sum(
,
2
)(
);
const
res4 = sum(
)(
);
const
res5 = sum(
)(
2
);
console
Output:
10
10
10
10
10
arguments
The solution for this is quite straightforward, all we have to do is update the
condition and instead of checking the number of
const
sum = (
...args
) => {
const
storage = [...args];
//base case
202
if
(storage.length ===
){
return
}
//closure
else
const
temp =
function
...args2
){
storage.push(...args2);
if
(args2.length ===
){
return
storage.reduce((
a, b
) => a + b,
);
else
return
temp;
return
temp;
Test Case
Input:
const
res = sum(
)();
const
res2 = sum(
)(
)(
)(
)();
const
res3 = sum(
)(
)();
const
res4 = sum(
)(
)();
const
res5 = sum(
)(
)();
const
res6 = sum();
console
Output:
10
10
10
10
10
0
203
Currying - Problem 2
Problem Statement -
Create a javascript function that will remember its previously passed values
and return the sum of the current and previous value.
Example
sum(
);
// 5
sum(
);
// 8
sum(
);
// 12
sum(
0
);
// 12
Javascript functions have access to the state (properties & methods) of its
parent function even after it is executed.
So to create a function that will return the sum of the previous values in
javascript we will use this technique of closure.
const
curry =
()
=> {
let
sum =
store
return
function
(
num = 0
){
sum += num;
return
sum;
};
};
204
We now call this function which will return the inner function and we can
use the returned inner function for our action.
Test Case
let
sum = curry();
console
.log(sum(
));
//5
console
.log(sum(
));
//8
console
.log(sum(
));
//12
console
.log(sum(
));
//12
console
.log(sum());
//12
205
Currying - Problem 3
Problem Statement -
add(
)(
).value() =
add(
)(
3
).value() =
add(
)(
)(
).value() =
add(
)(
)+
=
6;
This is a little tricky question and requires us to use and modify the
valueOf() method.
When JavaScript wants to turn an object into a primitive value, it uses the
valueOf() method. JavaScript automatically calls the valueOf() method
when it comes across an object where a primitive value is
Example
function
MyNumberType
number
){
this
.number = number;
MyNumberType.prototype.valueOf =
function
() {
return
this
.number +
};
const
myObj =
new
MyNumberType(
);
myObj +
// 8
Thus we can form a closure and track the arguments in an Array and
We will also override the valueOf() method and return the sum of all the
arguments for each primitive action, also add a new method
206
value() that will reference the valueOf() thus when invoked will return the
sum of arguments.
function
add
...current
){
let
sum = current;
function
resultFn
...rest
){
return
resultFn;
function
(){
return
sum.reduce((
acc, current
);
};
resultFn.value = resultFn.valueOf;
return
resultFn;
Test Case
Input:
console
.log(add(
)(
).value() ==
);
console
.log(add(
)(
).value() ==
);
console
.log(add(
1
)(
)(
).value() ==
);
console
.log(add(
)(
)+
);
Output:
true
true
true
6
207
Problem Statement -
Example
Input:
"12:10AM"
"12:33PM"
Output:
00
10
12
33
JavaScript string has method endsWith which can be used to check if the
time is ending with “AM” or “PM”.
Split the time into hours and minutes to easily convert them
const
formatTime = (
time
) => {
const
timeLowerCased = time.toLowerCase();
let
":"
);
// Special case
208
(timeLowerCased.endsWith(
"am"
)){
hours = hours ==
12
"0"
: hours;
else
if
(timeLowerCased.endsWith(
"pm"
)){
hours = hours ==
12
? hours :
String
(+hours
+
12
);
return
`${hours.padStart(
)}:${mins.slice(
-2
).padStart(
)}`
}
Test Case
Input:
console
.log(formatTime(
"12:10AM"
));
console
.log(formatTime(
"12:33PM"
));
Output:
00
10
12
33
209
Example
Input:
"00:00"
"12:33"
Output:
"12:00 AM"
"12:33 PM"
We will split the input string on “:” and get the hour and minutes.
Check if the hour is greater than 12 then reset the hour minusing 12
const
formatTime = (
time
) => {
time_splitted = time.split(
":"
);
// default is AM
let
ampm =
'AM'
if
(time_splitted[
] >=
12
){
ampm =
'PM'
}
© JavaScript Interview Guide | learnersbucket.com
210
if
(time_splitted[
]>
12
){
time_splitted[
] = time_splitted[
]-
12
if
(time_splitted[
0
] ==
){
time_splitted[
]=
12
return
time_splitted[
]+
':'
+ time_splitted[
+
''
+ ampm;
Test Case
Input:
console
.log(formatTime(
"12:33"
));
console
.log(formatTime(
"00:33"
));
Output:
"12:33 PM"
"12:33 AM"
211
Problem Statement -
Create a digital clock that shows the current time in HH:MM:SS
format.
Example
10
57
23
10
57
24
10
57
25
10
:
57
26
10
57
27
● getMinutes() - This will return the minutes of the hours in number format
like from (0 – 59).
We will create a function which will be using these methods to get the
current time in HH:MM:SS format.
const
clock =
()
=> {
const
time =
new
Date
(),
hours = time.getHours(),
minutes = time.getMinutes(),
seconds = time.getSeconds();
return
pad(hours) +
':'
+ pad(minutes) +
':'
+ pad(seconds);
212
}
We are using this pad() helper function which will format the input by
appending 0 if the number is a single digit.
const
pad = (
inp
) => {
return
String
(inp).length ==
'0'
+ inp : inp;
};
Now when we call the clock function it will return the single instance of
time at the moment it was called.
console
.log(clock());
//10:59:23
But to make it work like a clock we will need to call this function
repeatedly after 1 second. For this we will use the setInterval function
which repeatedly calls the function after a given interval of time.
setInterval(
function
() {
console
.log(clock());
},
1000
);
//10:59:23
//10:59:24
//10:59:25
//10:59:26
213
Problem Statement -
Example
Input:
,
10
Output:
[[
], [
], [
8
,
], [
10
]]
1. A normal function that will take the input and return the output.
2. We will extend the JavaScript array and add a new method chop,
Normal function
We will traverse till there is an element in the array, in each iteration slice
the sub-array of the given size and push them to the output array.
const
chop = (
) => {
//temp array
const
temp = [...arr];
//output
const
output = [];
let
i=
while
(i < temp.length) {
214
output.push(temp.slice(i, i + size));
i = i + size;
return
output;
Test Case
Input:
console
.log(chop([
,
10
],
));
Output:
[[
], [
], [
,
8
], [
10
]]
We can use the same logic and add a new method to the array by
Deep copy all the elements of the array so that we don’t mutate the
original array and if the size is not defined then return this deep copy, else
return the array of chunks.
Array
.prototype.chop =
function
size
){
//temp array
const
temp = [...this];
//if size is not defined
if
(!size){
return
temp;
//output
const
output = [];
let
i=
while
(i < temp.length) {
215
i = i + size;
return
output;
Test Case
Input:
const
arr = [
,
6
10
];
const
output = arr.chop(
);
console
.log(output);
Output:
[[
,
2
], [
], [
], [
10
]]
216
Chop string into chunks of given length
Problem Statement -
Write a function to chop string into chunks of given length and return it as
an array.
Example
Input:
'javascript'
Output:
'jav'
'asc'
'rip'
't'
1. Bruteforce.
2. Regex.
Bruteforce approach
const
chop = (
) => {
const
arr = [];
let
i=
while
(i < str.length) {
arr.push(str.slice(i, i + size));
i = i + size;
}
217
return
arr;
Test Case
Input:
console
.log(chop(
'javascript'
));
Output:
"jav"
"asc"
,
"rip"
"t"
Using Regex
Regex expression
str.match(
/.{1,n}/g
);
the substring
If the string contains any newlines or carriage returns, then use this
expression.
str.match(
/(.|[\r\n]){1,n}/g
);
const
chop = (
) => {
return
str.match(
new
RegExp
'.{1,'
+ size +
'}'
'g'
));
Test Case
Input:
© JavaScript Interview Guide | learnersbucket.com
218
console
.log(chop(
'javascript'
));
Output:
"jav"
"asc"
"rip"
"t"
219
Deep flatten object
Problem Statement -
Given an nested object which can have any type of object, deep flatten it
and return the new object in Javascript.
Example
Input:
A:
"12"
B:
23
C: {
P:
23
O: {
L:
56
},
Q: [
Output:
"A"
"12"
"B"
23
"C.O.L"
56
,
"C.P"
23
"C.Q.0"
"C.Q.1"
In the output if you notice, when we have nested objects, the key is
concatenated till there is a non-object value, similar for the array, the key is
concatenated on the index.
220
5. Otherwise, iterate the value and use the array’s index along with the
existing key as a new key and then store it in the output.
const
flatten = (
obj, prefix
) => {
let
output = {};
for
let
in
obj){
let
val = obj[k];
//new key
const
"."
+ k : k;
if
typeof
val ===
"object"
){
// if it is array
if
Array
.isArray(val)){
//use rest & spread together to convert
//array to object
const
{ ...arrToObj } = val;
const
//if it is object
else
const
// normal value
else
return
output;
Test Case
Input:
const
nested = {
A:
"12"
B:
23
C: {
P:
23
,
O: {
L:
56
},
Q: [
};
console
.log(flatten(nested));
Output:
"A"
"12"
"B"
:
23
"C.O.L"
56
"C.P"
23
"C.Q.0"
"C.Q.1"
}
© JavaScript Interview Guide | learnersbucket.com
222
Problem Statement -
But there are scenarios where we want to restrict this modification to some
limit or completely.
We will see two different ways in which we can achieve the same.
methods of the objects but cannot add a new one. Object.seal() can be used
to achieve the same but it also marks all existing properties as non-
configurable like we cannot delete them but just update their
value if it is writable.
const
obj = {
prop:
42
};
Object
.seal(obj);
obj.prop =
33
console
.log(obj.prop);
// 33
delete
obj.prop;
223
console
.log(obj.prop);
// 33
Object.seal() .
const
obj = {
prop:
42
nested: {
a:
b:
};
Object
.seal(obj);
obj.nested.a =
delete
obj.nested.a;
console
.log(obj.nested.a);
// undefined
However, we can create another helper function which will deep seal
function
deepSeal
object
){
let
propNames =
Object
.getOwnPropertyNames(object);
for
let
name
of
propNames) {
let
value = object[name];
typeof
value ===
"object"
deepSeal(value) : value;
return
Object
.seal(object);
224
const
obj = {
prop:
42
nested: {
a:
b:
};
deepSeal(obj);
obj.nested.a =
delete
obj.nested.a;
console
.log(obj.nested.a);
// 2
const
obj = {
prop:
42
nested: {
a:
b:
};
deepSeal(obj);
console
.log(
Object
.isSealed(obj));
//true
225
const
obj = {
prop:
42
};
Object
.freeze(obj);
obj.prop =
33
console
.log(obj.prop);
// 42
But this also only shallowly freezes the nested object properties.
const
obj = {
prop:
42
nested: {
a:
b:
};
Object
.freeze(obj);
obj.nested.a =
33
;
// Updates the value
console
.log(obj.nested.a);
// 33
Like deepSeal() we can also create a deepFreeze() function that will freeze
the nested objects as well.
226
function
deepFreeze
object
){
var
propNames =
Object
.getOwnPropertyNames(object);
for
(
let
name
of
propNames) {
let
value = object[name];
typeof
value ===
"object"
deepFreeze(value) : value;
return
Object
.freeze(object);
const
obj = {
prop:
42
nested: {
a:
b:
};
deepFreeze(obj);
obj.nested.a =
33
console
.log(obj.nested.a);
// 1
const
obj = {
prop:
42
nested: {
a:
b:
227
};
deepFreeze(obj);
console
.log(
Object
.isFrozen(obj));
//true
228
Merge objects
Problem Statement -
native methods.
There are two different types of merge that can be performed on the
objects.
1. Shallow : In shallow merge only the properties owned by the object will
be merged, it will not merge the extended properties
or methods.
Shallow merging
We will iterate all the keys of the source object and check if the
property belongs to the source object then only copy it to the target.
let
merge = (
...arguments
) => {
// Create a new object
let
target = {};
let
merger = (
obj
) => {
for
let
prop
in
obj) {
if
(obj.hasOwnProperty(prop)) {
target[prop] = obj[prop];
}
};
for
let
i=
;i<
arguments
.length; i++) {
229
merger(
arguments
[i]);
return
target;
Test Case 1
Input:
let
obj1 = {
name:
'prashant'
age:
23
let
obj2 = {
qualification:
'BSC CS'
loves:
'Javascript'
let
.log(merged);
Output:
/*
Object {
age: 23,
loves: "Javascript",
name: "prashant",
*/
Test Case 2
In this if you notice the skills value is overridden by the last object.
Input:
let
obj1 = {
name:
'prashant'
age:
23
skills : {programming:
'JavaScript'
230
let
obj2 = {
qualification:
'BSC CS'
loves:
'Javascript'
skills : {sports:
'swimming'
}
let
console
.log(merged);
Output:
/*
Object {
age: 23,
loves: "Javascript",
OceanofPDF.com
name: "prashant",
skills: {
sports:
"swimming"}
*/
Deep merging
let
merge = (
...arguments
) => {
// Variables
let
target = {};
let
merger = (
obj
) => {
for
let
prop
in
obj) {
if
(obj.hasOwnProperty(prop)) {
if
Object
.prototype.toString.call(obj[prop])
===
'[object
Object]'
){
is an object
© JavaScript Interview Guide | learnersbucket.com
231
else
target[prop] = obj[prop];
};
for
let
i=
;i<
arguments
.length; i++) {
merger(
arguments
[i]);
return
target;
};
Test Case
In this if you notice the nested objects with different values are
merged.
let
obj1 = {
name:
'prashant'
age:
23
nature: {
"helping"
true
"shy"
false
let
obj2 = {
qualification:
'BSC CS'
loves:
'Javascript'
nature: {
"angry"
:
false
"shy"
true
console
.log(merge(obj1, obj2));
/*
232
Object {
age: 23,
loves: "Javascript",
name: "prashant",
nature: Object {
angry: false,
helping: true,
shy: true
},
*/
We can combine both the function for shallow copy and deep copy
together to create a single function which will perform merge based on the
arguments passed.
If we will pass true as first argument then it will perform deep merge else it
will perform shallow merge.
let
merge = (
...arguments
) => {
// Variables
let
target = {};
let
deep =
false
;
let
i=
if
typeof
arguments
]) ===
'boolean'
){
deep =
arguments
];
i++;
let
merger = (
obj
) => {
for
let
prop
in
obj) {
if
(obj.hasOwnProperty(prop)) {
if
(deep &&
Object
.prototype.toString.call(obj[prop])
===
© JavaScript Interview Guide | learnersbucket.com
233
'[object Object]'
){
is
an object
else
target[prop] = obj[prop];
};
for
(; i <
arguments
.length; i++) {
merger(
arguments
[i]);
return
target;
};
Test Case
let
obj1 = {
name:
'prashant'
age:
23
nature: {
"helping"
:
true
"shy"
false
let
obj2 = {
qualification:
'BSC CS'
loves:
'Javascript'
nature: {
"angry"
false
,
"shy"
true
//Shallow merge
console
.log(merge(obj1, obj2));
234
/*
Object {
age: 23,
loves: "Javascript",
name: "prashant",
nature: Object {
angry: false,
shy: true
},
qualification: "BSC CS"
*/
//Deep merge
console
.log(merge(
true
, obj1, obj2));
/*
Object {
age: 23,
loves: "Javascript",
name: "prashant",
nature: Object {
angry: false,
helping: true,
shy: true
},
}
*/
235
Problem Statement -
You must be familiar with browser history and its functionality where you
can navigate through the browsed history. Implement the same.
Example
Input:
const
bh =
new
BrowserHistory();
bh.visit(
'A'
);
console
.log(bh.current());
bh.visit(
'B'
);
console
.log(bh.current());
bh.visit(
'C'
);
console
.log(bh.current());
bh.goBack();
console
.log(bh.current());
bh.visit(
'D'
);
console
.log(bh.current());
Output:
"A"
"B"
"C"
"B"
"D"
We can implement this with the help of an array and index tracker for
navigation.
236
For each visit, add the URL at the next index. while navigating backward
return the URL of the previous index, going forward return
the URL at the next index. Add checks to prevent the under and
function
BrowserHistory
() {
// track history
this
.history = [];
this
.index =
-1
this
.visit =
function
url
){
this
.history[++
this
.index] = url;
this
.current =
function
() {
return
this
.history[
this
.index];
// go to previous entry
this
.backward =
function
() {
this
.index =
Math
.max(
, --
this
.index);
}
// go to next entry
this
.forward =
function
() {
this
.index =
Math
.min(
this
.history.length -
++
this
.index);
Test Case
Input:
const
bh =
new
BrowserHistory();
bh.visit(
'A'
);
console
.log(bh.current());
237
bh.visit(
'B'
);
console
.log(bh.current());
bh.visit(
'C'
);
console
.log(bh.current());
bh.backward();
console
.log(bh.current());
bh.visit(
'D'
);
console
.log(bh.current());
bh.backward();
console
.log(bh.current());
bh.forward();
console
.log(bh.current());
Output:
"A"
"B"
"C"
"B"
"D"
"B"
"D"
238
Problem Statement -
interface (class or function) and the same object is returned every time
when called.
Example
const
object1 = singleton.getInstance();
const
object2 = singleton.getInstance();
console
variable that stores the created instance and returns it every time.
const
Singleton = (
function
() {
let
instance;
function
createInstance
() {
const
object =
new
Object
);
return
object;
return
getInstance:
function
() {
if
(!instance) {
instance = createInstance();
return
instance;
239
};
})();
Test Case
const
object1 = singleton.getInstance();
const
object2 = singleton.getInstance();
console
//true
240
Problem Statement -
For example, when a click event is triggered you can access the event
object to get all the event details about the click like its position on the
screen, etc.
You can also remove the listener (unsubscribe) to stop listening if you want.
participants.
241
1. Host
2. Observer
Keeping these two things in mind, we can create the Observer design
pattern in JavaScript.
const
Move =
function
(){
this
.handlers = [];
this
.subscribe =
function
fn
){
this
.handlers.push(fn);
};
this
.unsubscribe =
function
fn
){
this
.handlers =
this
.handlers.filter((
item
) =>
};
this
.fire =
function
o, thisObj
){
const
scope = thisObj ||
window
this
.handlers.forEach((
item
) => {
item.call(scope, o);
});
Test Case
Input:
// 1st observer
const
moveHandler =
function
item
){
console
.log(
"fired: "
+ item);
};
// 2nd observer
const
moveHandler2 =
function
item
){
console
.log(
"Moved: "
+ item);
};
const
move =
new
Move();
move.subscribe(moveHandler);
move.fire(
'event #1'
);
move.unsubscribe(moveHandler);
move.fire(
'event #2'
);
move.subscribe(moveHandler);
move.subscribe(moveHandler2);
move.fire(
'event #3'
);
Output:
243
Implement groupBy() method
Problem Statement -
Write the polyfill for the groupBy() method that accepts a collection and
iteratee as arguments and returns the object that has grouped the collection
values using iteratee’s value as the key.
Example
Input:
groupBy([
6.1
4.2
6.3
],
Math
.floor);
groupBy([
"one"
"two"
,
"three"
],
"length"
);
Output:
The whole logic can be abstracted inside the Array.reduce() method and we
can easily aggregate the values.
Here the iteratee can be a function or a property, thus we will have to check
the type of iteratee, and depending upon that we can get the key from it.
const
groupBy = (
values, keyFinder
) => {
return
values.reduce((
a, b
) => {
const
key =
typeof
keyFinder ===
'function'
keyFinder(b) :
b[keyFinder];
if
(!a[key]){
a[key] = [b];
else
244
return
a;
}, {});
};
Test Case
Input:
console
.log(groupBy([
6.1
4.2
6.3
],
Math
.floor));
console
.log(groupBy([
"one"
,
"two"
"three"
],
"length"
));
Output:
245
Problem Statement -
The most basic approach is to convert the whole array or the object to a
string then compare if those strings are equal or not.
To convert an array or object we will be using JSON.stringify() .
let
a = {a:
, b:
, c:
};
let
b = {a:
, b:
, c:
};
//"{'a':1,'b':2,'c':3}" "{'a':1,'b':2,'c':3}"
console
.log(
JSON
.stringify(a) ===
JSON
.stringify(b));
//true
This may seem to be working fine, but this approach fails when we
let
a = {a:
, b:
, c:
};
let
b = {b:
, a:
1
, c:
};
//"{'a':1,'b':2,'c':3}" "{'b':2,'a':1,'c':3}"
console
.log(
JSON
.stringify(a) ===
JSON
.stringify(b));
//false
246
object’s methods on it. Object.keys([1, 2]) will return the indexes as key
and thus we can use it to get the values from the array.
● Iterate the keys, if the values of both the objects for the given keys are
objects, recursively call the same function with values
and deep check, if they are not equal, return false.
● If the values are non-iterable, do the equality check, if they are not equal
return false.
const
deepEqual = (
object1, object2
) => {
const
keys1 =
Object
.keys(object1);
const
keys2 =
Object
.keys(object2);
// if mismatched keys
if
return
false
for
const
key
of
keys1) {
const
val1 = object1[key];
const
val2 = object2[key];
const
typeof
val1 ===
"object"
&& val2 &&
typeof
val2 ===
"object"
// if are objects
if
(areObjects){
if
(!deepEqual(val1, val2)){
247
return
false
if
return
false
return
true
Test Case
Input:
const
obj1 = {
name:
"learnersbucket"
details: {
x: [
],
y:
},
};
const
obj2 = {
name:
"learnersbucket"
details: {
y:
x: [
1
],
},
};
console
.log(deepEqual(obj1, obj2));
Output:
true
248
Problem Statement -
method that will return the next array value on each invocation.
Example
let
iterator = helper([
1
,
"hello"
]);
console
.log(iterator.next());
// 1
console
.log(iterator.next());
// 2
console
.log(iterator.done());
// false
console
.log(iterator.next());
// "hello"
console
.log(iterator.done());
// true
console
.log(iterator.next());
// "null"
Create an index tracker in the outer function that will help to return the next
value from the next() method.
In the done() return boolean flag depending upon the index position on the
input array’s size.
const
helper = (
array
) => {
let
nextIndex =
return
{
// return the next value
// or null
next:
function
() {
return
? array[nextIndex++]
null
249
},
done:
function
() {
return
nextIndex >= array.length;
};
};
Test Case
let
iterator = helper([
"hello"
]);
console
.log(iterator.next());
// 1
console
.log(iterator.next());
// 2
console
.log(iterator.done());
// false
console
.log(iterator.next());
// "hello"
console
.log(iterator.done());
// true
console
.log(iterator.next());
// "null"
250
Problem Statement -
Example
Input:
const
arr = [];
arr.addListener(
'add'
console
.log(
, items);
});
arr.addListener(
'remove'
=> {
console
.log(item,
);
});
arr.pushWithEvent(
'add'
,[
]);
arr.popWithEvent(
'remove'
);
Output:
We will extend the array prototype and add the required methods to it
● listeners : This will store the list of event listeners associated with the
event name.
251
functions.
Array
.prototype.listeners = {};
Array
.prototype.addListener =
function
(
name, callback
){
triggered
if
(!
this
.listeners[name]) {
this
.listeners[name] = [];
this
.listeners[name].push(callback);
Array
.prototype.pushWithEvent =
function
event, args
this
.push(...args);
this
.triggerEvent(event, args);
};
Array
.prototype.popWithEvent =
function
event, args
)
{
const
element =
this
.pop();
this
.triggerEvent(event, element);
};
252
Array
.prototype.triggerEvent =
function
eventName, elements
){
this
.listeners[eventName]) {
this
.listeners[eventName].forEach(
callback
=>
callback(eventName, elements,
this
);
};
Array
.prototype.removeListener =
function
eventName,
callback
){
// if event exists
if
this
.listeners[eventName]){
this
.listeners[eventName] =
this
.listeners[eventName].filter((
=> e
!== callback);
Test Case
Input:
const
arr = [];
const
onAdd = (
) => {
console
.log(
, items);
const
onAddAgain = (
) => {
console
.log(
, items);
}
arr.addListener(
'add'
, onAdd);
arr.addListener(
'add'
, onAddAgain);
arr.addListener(
'remove'
=> {
console
.log(item,
);
});
arr.pushWithEvent(
'add'
,[
,
2
'a'
'b'
]);
253
arr.removeListener(
'add'
, onAddAgain);
// removes
arr.pushWithEvent(
'add'
,[
,
5
]);
arr.popWithEvent(
'remove'
);
console
.log(arr);
Output:
"a"
"b"
]
"a"
"b"
5
]
"a"
"b"
254
Example
const
arr = [
{ name:
"Amir"
, id:
"1"
},
{ name:
"Samlan"
, id:
"2"
},
{ name:
"Shahrukh"
, id:
"0"
},
];
console
.log(filterObject(arr,
));
// { name: "Amir",
id: "1" }
console
.log(filterObject(arr,
"Amir"
));
// { name:
console
.log(filterObject(arr,
"0"
));
// { name: "Shahrukh",
id: "0" }
One way to solve this is by using the Proxy and overriding the get method,
but the problem with using this is it converts the input values to a string,
thus it is not easy to determine if we have to filter on index or value.
that will accept the array of objects and filter value as input and based on
the type of filter, it will check in the object and return the
const
filterObject = (
arr, filter
) => {
if
typeof
filter ===
"string"
){
for
(
const
entry
of
arr){
for
const
[key, val]
of
Object
.entries(entry)){
if
return
entry;
255
}
}
else
if
(filter
in
arr){
return
arr[filter];
// if nothing is found
else
return
undefined
};
Test Case
Input:
const
arr = [
{ name:
"Amir"
, id:
"1"
},
{ name:
"Samlan"
, id:
"2"
},
{ name:
"Shahrukh"
, id:
"0"
},
];
console
.log(filterObject(arr,
));
console
.log(filterObject(arr,
"Amir"
));
console
.log(filterObject(arr,
"0"
));
console
.log(filterObject(arr,
"-1"
));
Output:
// undefined
© JavaScript Interview Guide | learnersbucket.com
256
Problem Statement -
Given an array of objects and two keys “on” and “who”, aggregate the
Example
Input:
const
endorsements = [
{ skill:
'css'
, user:
'Bill'
},
{ skill:
'javascript'
, user:
'Chad'
},
{ skill:
'javascript'
, user:
'Bill'
},
{ skill:
'css'
, user:
'Sue'
},
{ skill:
'javascript'
, user:
'Sue'
},
{ skill:
'html'
, user:
'Sue'
}
];
console
.log(aggregate(endorsements,
"user"
"skill"
));
Output:
"user"
"Bill"
"skill"
:[
"css"
"javascript"
]
},
"user"
"Chad"
"skill"
:[
"javascript"
},
"user"
"Sue"
"skill"
:[
"css"
,
"javascript"
"html"
257
● Get the value of the “ on ” key and aggregate the values of the
const
aggregate = (
) => {
const
agg = arr.reduce((
a, b
) => {
const
onValue = b[on];
const
whoValue = b[who];
if
(a[onValue]){
a[onValue] = {
[on]: onValue,
else
a[onValue] = {
[on]: onValue,
[who]: [whoValue]
258
return
a;
}, {});
return
Object
.values(agg);
Test Case
Input:
const
endorsements = [
{ skill:
'css'
, user:
'Bill'
},
{ skill:
'javascript'
, user:
'Chad'
},
{ skill:
'javascript'
, user:
'Bill'
},
{ skill:
'css'
, user:
'Sue'
},
{ skill:
'javascript'
, user:
'Sue'
},
{ skill:
'html'
, user:
'Sue'
];
console
.log(aggregate(endorsements,
"skill"
"user"
));
Output:
[{
"skill"
:
"css"
"user"
:[
"Bill"
"Sue"
},
"skill"
"javascript"
"user"
:[
"Chad"
"Bill"
,
"Sue"
},
"skill"
"html"
"user"
:[
"Sue"
259
}]
260
Problem Statement -
Given an array with two entries, parent and child relation, convert the array
to a relation string (parent -> child -> grandchild).
The input array will contain relations for many ancestries in random order,
We must return the array of strings representing different relationships.
Input:
"lion"
"cat"
],
"cat"
"mammal"
],
"dog"
"mammal"
],
[
"mammal"
"animal"
],
"fish"
"animal"
],
"shark"
"fish"
],
];
Output:
261
const
aggregate = (
arr
) => {
return
arr.reduce((
a, b
) => {
const
[child, parent] = b;
// aggregating on child
a[child] = parent;
return
a;
}, {});
};
const
arr = [
"lion"
"cat"
],
"cat"
,
"mammal"
],
"dog"
"mammal"
],
"mammal"
"animal"
],
"fish"
"animal"
],
"shark"
,
"fish"
],
];
console
.log(aggregate(arr));
/*
"lion": "cat",
"cat": "mammal",
"dog": "mammal",
"mammal": "animal",
"fish": "animal",
"shark": "fish"
*/
We have aggregated the values on the child as one child will have only one
parent, this way a single key-value pair is formed which will be faster to
process and in forming relationships.
Now, all we have to do is keep on traversing this map and form the
relation string. For this, we will get the value of the current key and
recursively call the same function with this value and the map to get its
ancestry and so on.
const
convert = (
obj
) => {
return
Object
.keys(obj).reduce((
a, b
) => {
a.push(getKey(obj, b));
return
a;
}, []);
};
const
getKey = (
obj, key
) => {
// access the
const
val = obj[key];
if
(val
in
obj){
return
getKey(obj, val) +
+ key;
else
{
return
val +
+ key;
};
const
map = {
"lion"
"cat"
"cat"
"mammal"
"dog"
"mammal"
,
"mammal"
"animal"
"fish"
"animal"
"shark"
"fish"
};
console
.log(convert(map));
/*
263
*/
Combining it together.
const
ancestry = (
arr
) => {
const
aggregate = (
arr
) => {
return
arr.reduce((
a, b
) => {
const
[child, parent] = b;
// aggregating on child
a[child] = parent;
return
a;
}, {});
};
const
convert = (
obj
) => {
return
Object
.keys(obj).reduce((
a, b
) => {
a.push(getKey(obj, b));
return
a;
}, []);
};
const
getKey = (
obj, key
) => {
// access the
const
val = obj[key];
264
if
(val
in
obj){
return
getKey(obj, val) +
+ key;
else
return
val +
+ key;
};
const
aggregatedMap = aggregate(arr);
return
convert(aggregatedMap);
};
Test Case
Input:
const
arr = [
"lion"
"cat"
],
"cat"
"mammal"
],
"dog"
"mammal"
],
"mammal"
"animal"
],
"fish"
"animal"
],
"shark"
"fish"
],
];
console
.log(ancestry(arr));
Output:
[
265
Problem Statement -
Input:
const
obj = {
a: {
b: {
c: [
};
console
.log(get(obj,
'a.b.c'
));
console
.log(get(obj,
'a.b.c.0'
));
console
.log(get(obj,
'a.b.c[1]'
));
console
.log(get(obj,
'a.b.c[3]'
));
Output:
// [1,2,3]
// 1
// 2
// undefined
266
● Once we have the exact path, deeply traverse the input nested object to
find the value.
const
get = (
obj, path
) => {
if
(path ===
''
|| path.length ==
return
undefined
if
Array
'.'
);
let
exactPath = [];
for
let
i=
if
(path[i] !==
'['
&& path[i] !==
']'
&& path[i]
!==
'.'
){
exactPath.push(path[i]);
const
value = exactPath.reduce((
source, path
) =>
source[path], obj);
return
value ? value :
undefined
;
};
Test Case
Input:
const
obj = {
a: {
b: {
c: [
};
console
.log(get(obj,
'a.b.c'
));
console
.log(get(obj,
'a.b.c.0'
));
console
.log(get(obj,
'a.b.c[1]'
));
console
.log(get(obj, [
'a'
'b'
'c'
'2'
]));
console
.log(get(obj,
'a.b.c[3]'
));
267
console
.log(get(obj,
'a.c'
));
Output:
// [1,2,3]
// 1
// 2
// 3
// undefined
// 'bfe'
268
Problem Statement -
Given an object, a path in the string or array of strings format, and a value,
update the value at the given path in the object.
lodash._get() method.
Example
const
object = {
'a'
: [{
'b'
:{
'c'
} }] };
set(object,
'a[0].b.c'
);
console
.log(object.a[
].b.c);
// 4
set(object, [
'x'
'0'
'y'
'z'
],
);
console
.log(object.x[
].y.z);
// 5
To implement this function, we will first check if the provided path is a
string or an array of strings.
If it is a string then filter all the special characters like [, ] and split the
string on dot(.) to get all the path keys in an array.
Then using a helper function we can assign the value to the provided path.
● Get only the first key from the path array and aggregate the rest of the
keys.
● If there are no more keys left to update, assign the value to the current
key.
269
● Else recursively call the same function with the current value for the next
path.
Note:- This will override the existing value and assign a new one.
const
helper = (
) => {
let
if
(rest.length >
){
if
(!obj[current]){
// add an array
const
isNumber =
`${+rest[
0
]}`
=== rest[
];
key
if
typeof
obj[current] !==
'object'
){
isNumber =
`${+rest[
]}`
=== rest[
];
else
270
else
{
obj[current] = value;
return
obj;
const
set = (
) => {
let
pathArr = path;
array
if
typeof
path ===
'string'
){
pathArr = path.replace(
'['
'.'
).replace(
']'
''
).split(
"."
);
};
Test Case
Input:
const
abc = {
a: {
b: {
c: [
},
d: {
a:
"hello"
};
const
instance1 =
JSON
.parse(
JSON
.stringify(abc));
set(instance1,
'a.b.c'
'learnersbucket'
);
console
.log(instance1.a.b.c);
271
const
instance2 =
JSON
.parse(
JSON
.stringify(abc));
set(instance2,
'a.b.c.0'
'learnersbucket'
);
console
.log(instance2.a.b.c[
]);
const
instance3 =
JSON
.parse(
JSON
.stringify(abc));
set(instance3,
'a.b.c[1]'
'learnersbucket'
);
console
.log(instance3.a.b.c[
]);
const
instance4 =
JSON
.parse(
JSON
.stringify(abc));
set(instance4, [
'a'
'b'
'c'
'2'
],
'learnersbucket'
);
console
.log(instance4.a.b.c[
]);
const
instance5 =
JSON
.parse(
JSON
.stringify(abc));
set(instance5,
'a.b.c[3]'
'learnersbucket'
);
console
.log(instance5.a.b.c[
])
const
instance6 =
JSON
.parse(
JSON
.stringify(abc));
set(instance6,
'a.c.d[0]'
'learnersbucket'
);
console
.log(instance6.a.c.d[
]);
const
instance7 =
JSON
.parse(
JSON
.stringify(abc));
set(instance7,
'a.d.01'
'learnersbucket'
);
console
.log(instance7.a.d[
'01'
]);
const
object = {
'a'
: [{
'b'
:{
'c'
3
} }] };
set(object,
'a[0].b.c'
);
console
.log(object.a[
].b.c);
set(object, [
'x'
'0'
'y'
'z'
],
5
);
console
.log(object.x[
].y.z);
Output:
"learnersbucket"
"learnersbucket"
"learnersbucket"
"learnersbucket"
"learnersbucket"
"learnersbucket"
"learnersbucket"
272
273
Example
console
.log(
JSON
.stringify([{ x:
, y:
}]));
string.
● For number , if the value is finite return the value as it is else return
"null".
● For boolean return it as it is and for string return the value in double
quotes.
● The last thing left is the object, there are multiple cases to handle for the
object.
stringify further.
274
// helper method
static
value(val) {
switch
typeof
val) {
case
'boolean'
case
'number'
number as it is
return
isFinite
(val) ?
`${val}`
`null`
case
'string'
return
`"${val}"`
case
'function'
case
'symbol'
case
'undefined'
return
'null'
case
'object'
:
// if the value is date, convert date to string
if
(val
instanceof
Date
){
return
`"${val.toISOString()}"`
// new String(value)
else
if
(val.constructor ===
String
){
return
`"${val}"`
;
}
as constructor,
else
if
(val.constructor ===
Number
|| val.constructor
===
Boolean
){
return
isFinite
(val) ?
`${val}`
`null`
}
// if value is a array, return key values
as
else
if
Array
.isArray(val)) {
return
`[${val.map(value =>
this
.value(value)).join(',')}]`
275
return
this
.stringify(val);
}
Second, we will handle some base cases like, if it is a null value, return
'null ', if it is an object, get the appropriate value from the above method. At
the end wrap the value inside curly braces and return
them.
// main method
static
stringify(obj) {
undefined or an array
value
if
typeof
obj !==
'object'
|| obj ===
undefined
|| obj
instanceof
Array
){
return
this
.value(obj);
else
if
(obj ===
null
){
return
`null`
// if it exists
this
.removeCycle(obj);
let
objString =
Object
.keys(obj).map((
) => {
return
typeof
obj[k] ===
'function'
)?
null
`"${k}": ${
this
.value(obj[k])}`
;
});
return
`{${objString}}`
The final case to handle the circular object, for that we will be using the
removeCycle(obj) method to remove the cycle from the objects.
276
static
removeCycle = (
obj
) => {
//set store
const
set =
new
WeakSet
([obj]);
//recursively detects and deletes the object
references
function
iterateObj
obj
){
for
let
key
in
obj) {
chain
if
(obj.hasOwnProperty(key)) {
if
(
typeof
obj[key] ===
'object'
){
// then delete it
if
(set.has(obj[key])){
delete
obj[key];
else
set.add(obj[key]);
next objects
iterateObj(obj[key]);
}
}
})(obj);
Complete code
class
JSON
// main method
static
stringify(obj) {
undefined or an array
value
if
typeof
obj !==
'object'
|| obj ===
undefined
|| obj
instanceof
Array
){
return
this
.value(obj);
else
if
(obj ===
null
){
277
return
`null`
;
// if it exists
this
.removeCycle(obj);
let
objString =
Object
.keys(obj).map((
) => {
return
typeof
obj[k] ===
'function'
)?
null
:
`"${k}": ${
this
.value(obj[k])}`
});
return
`{${objString}}`
// helper method
static
value(val) {
switch
typeof
val) {
case
'boolean'
case
'number'
number as it is
return
isFinite
(val) ?
`${val}`
`null`
case
'string'
return
`"${val}"`
case
'function'
case
'symbol'
case
'undefined'
return
'null'
case
'object'
:
// if the value is date, convert date to string
if
(val
instanceof
Date
){
return
`"${val.toISOString()}"`
// new String(value)
278
else
if
(val.constructor ===
String
){
return
`"${val}"`
as constructor,
else
if
(val.constructor ===
Number
|| val.constructor
===
Boolean
){
return
isFinite
(val) ?
`${val}`
`null`
;
else
if
Array
.isArray(val)) {
return
`[${val.map(value =>
this
.value(value)).join(',')}]`
return
this
.stringify(val);
}
}
static
removeCycle = (
obj
) => {
//set store
const
set =
new
WeakSet
([obj]);
references
function
iterateObj
obj
){
for
let
key
in
obj) {
chain
if
(obj.hasOwnProperty(key)) {
if
typeof
obj[key] ===
'object'
){
// then delete it
if
(set.has(obj[key])){
delete
obj[key];
else
set.add(obj[key]);
next objects
iterateObj(obj[key]);
279
})(obj);
};
Test Case
Input:
let
obj1 = {
a:
b: {
c:
d:
-3
e: {
f: {
g:
-4
},
},
h: {
i:
j:
},
let
obj2 = {
a:
b: {
c:
'Hello World'
d:
2
e: {
f: {
g:
-4
},
},
h:
},
// cricular object
280
const
List =
function
(
val
){
this
.next =
null
this
.val = val;
};
const
item1 =
new
List(
10
);
const
item2 =
new
List(
20
);
const
item3 =
new
List(
30
);
item1.next = item2;
item2.next = item3;
item3.next = item1;
console
.log(
JSON
.stringify(item1));
console
.log(
JSON
.stringify(obj1));
console
.log(
JSON
.stringify(obj2));
console
.log(
JSON
.stringify([{ x:
, y:
}]));
console
.log(
JSON
.stringify([
new
Number
3
),
new
String
'false'
),
new
Boolean
false
),
new
Number
Infinity
)]));
console
.log(
JSON
.stringify({ x: [
10
undefined
function
(){},
Symbol
''
)]}));
console
.log(
JSON
.stringify({a:
Infinity
}));
Output:
"{'a': 1,'b': {'c': 'Hello World','d': 2,'e': {'f': {'g': -4}},'h': 'Good Night
Moon'}}"
"[3,'false',false,null]"
281
"{'x': [10,null,null,null]}"
"{'a': null}"
282
Problem Statement -
Example
const
json =
'{"result":true, "count":42}'
const
obj =
JSON
.parse(json);
console
.log(obj);
2. If the string is empty or has invalid characters like starting with single
quotes, then throw an error.
3. If the string has only [] , {} , true , false , or number , convert and return
the respective values.
4. The last part is to handle the nested values like arrays, objects, and arrays
of objects. For this, we will first get their individual
and {}).
static
parse(string) {
283
string = string.replace(
/ /g
''
);
switch
(string) {
case
''
throw
new
Error
();
case
'null'
return
null
case
'{}'
return
{};
case
'[]'
return
[];
case
'true'
return
true
;
case
'false'
return
false
default
if
return
Number
(string);
else
if
(string[
0
] ===
'\''
){
throw
new
Error
();
else
if
(string[
] ===
'\"'
){
return
string.slice(
1
-1
);
else
// if [] || {}
const
innerString = string.slice(
-1
);
// array of pairs if {}
const
subStrings =
this
.stringSplitByComma(innerString);
// if it is array
if
(string[
] ===
'['
){
284
return
subStrings.map(
item
=>
this
.parse(item));
else
if
(string[
] ===
'{'
){
// if it object
on :
return
subStrings.reduce((
acc, item
) =>
if
(item.indexOf(
':'
)>
-1
){
const
index = item.indexOf(
':'
);
const
thisKey = item.substring(
index);
const
thisValue = item.substring(index
);
acc[
this
.parse(thisKey)] =
this
.parse(thisValue);
}
return
acc;
}, {});
1. Track the opening and closing parentheses [] as well as the curly braces
{} .
2. Whenever a comma is spotted while traversing the string, get the value
between the opening and closing parentheses and/or curly
braces and push it into an array so that each of these values can
be parsed individually.
// helper function
objects
static
stringSplitByComma(string) {
const
allStrs = [];
285
let
lParen =
, lCurly =
let
left =
, right =
while
const
rChar = string[right];
if
(rChar ===
'['
) lParen++;
if
(rChar ===
'{'
) lCurly++;
if
(rChar ===
']'
) lParen--;
if
(rChar ===
'}'
) lCurly--;
// if a comma is spotted
if
((rChar ===
','
&& lCurly
===
) ||
const
allStrs.push(thisStr);
left = right +
1
right++;
return
allStrs;
Complete code
class
JSON
static
parse(string) {
string = string.replace(
/ /g
''
);
// convert each value accordingly
switch
(string) {
case
''
286
throw
new
Error
();
case
'null'
return
null
case
'{}'
:
return
{};
case
'[]'
return
[];
case
'true'
return
true
case
'false'
return
false
;
default
if
return
Number
(string);
else
if
(string[
] ===
'\''
){
throw
new
Error
();
else
if
(string[
] ===
'\"'
){
return
string.slice(
-1
);
else
{
// if [] || {}
const
innerString = string.slice(
-1
);
// array of pairs if {}
const
subStrings =
this
.stringSplitByComma(innerString);
// if it is array
if
(string[
] ===
'['
){
return
subStrings.map(
item
=>
this
.parse(item));
else
if
(string[
] ===
'{'
){
// if it object
on :
© JavaScript Interview Guide | learnersbucket.com
287
return
subStrings.reduce((
acc, item
) =>
if
(item.indexOf(
':'
)>
-1
){
const
index = item.indexOf(
':'
);
const
thisKey = item.substring(
0
index);
const
thisValue = item.substring(index
);
acc[
this
.parse(thisKey)] =
this
.parse(thisValue);
return
acc;
}, {});
}
}
// helper function
objects
static
stringSplitByComma(string) {
const
allStrs = [];
let
lParen =
, lCurly =
let
left =
, right =
0
while
const
rChar = string[right];
if
(rChar ===
'['
) lParen++;
if
(rChar ===
'{'
) lCurly++;
if
(rChar ===
']'
) lParen--;
if
(rChar ===
'}'
) lCurly--;
// if a comma is spotted
if
((rChar ===
','
&& lCurly
===
) ||
const
allStrs.push(thisStr);
left = right +
right++;
return
allStrs;
Test Case
console
.log(
JSON
.parse(
'[{"result":true,"count":42}]'
));
/*
"result": true,
"count": 42
*/
console
.log(
JSON
.parse(
30},"val": 20},"val": 10
}'
));
/*
{
"next": {
"next": {
"val": 30
},
"val": 20
},
"val": 10
*/
console
.log(
JSON
.parse(
));
/*
289
{
"a": 1,
"b": {
"c": 2,
"d": -3,
"e": {
"f": {
"g": -4
},
"h": {
"i": 5,
"j": 6
*/
console
.log(
JSON
.parse(
));
/*
"a": 1,
"b": {
"c": "HelloWorld",
"d": 2,
"e": {
"f": {
"g": -4
},
"h": "GoodNightMoon"
*/
console
.log(
JSON
.parse(
));
/*
"x": 5,
"y": 6
290
*/
console
.log(
JSON
.parse(
'[3,"false",false,null]'
));
/*
3,
"false",
false,
null
*/
console
.log(
JSON
.parse(
'{"x": [10,null,null,null]}'
));
/*
"x": [
10,
null,
null,
null
*/
console
.log(
JSON
.parse(
'[]'
));
/*
[]
*/
291
Problem Statement -
string.
Example
Input:
const
str =
'Hello, world'
const
styleArr = [[
'i'
], [
,
'b'
], [
10
'u'
]];
Output:
'<i>Hel</i>l<b>o, w<u>orl</u></b><u>d</u>'
Note – <u> tag gets placed before the <b> tag and after it as the insertion
index overlaps it.
WYSIWYG editors, where you write normal text and apply styles to it and
it will be converted to HTML encoding at the end.
Let us first see the second method as most of the times in the interview you
will be asked to implement this way only.
Custom function for HTML encoding of the string
The styles can be overlapping thus one tag can overlap the other and in such
scenarios we have to close the overlapping tags and re-open it again.
292
Input:
const
str =
"Hello, World"
const
style = [
'i'
],[
3
,
'b'
];
Output:
<i>
<b>
el
</b></i><b>
</b>
o, World
We can solve this with the help of a priority queue and a stack .
● Aggregate the styles on the starting index in the order of priority of the
range difference between them. (endIndex – startIndex),
entry back to the stack and in the next iteration create this tag.
● At the end return the string of the top most stack entry.
Priority queue
Queue, rather a helper function that adds a new item in the array and sorts it
in the ascending or descending order depending upon the
order.
function
addAndSort
){
if
track[index].sort((
a, b
};
Stack
function
Stack
() {
let
items = [];
let
top =
this
.push =
function
(
element
){
items[top++] = element;
};
this
.pop =
function
() {
return
items[--top];
};
this
.peek =
function
() {
return
items[top -
1
];
};
};
Tag method
A helper function to create a new tag for each entry of styles and its
corresponding, also helps to get the range for easier processing in the
priority queue. We push this in the priority queue and sort it.
function
Tag
){
this
.start = start;
this
.end = end;
this
.tag = tag;
this
.text =
""
294
this
.getRange =
()
=> {
return
this
.end -
this
.start;
};
};
● On the start index of the styles aggregate the common tags in the priority
queue based on the difference of their range in
descending order.
● Add these priority queues at the start indexes of the styles in the trace .
● Create a new stack and add an initial Tag with maximum range
● Iterate till input string length, get the tags from the trace at each index,
iterate the prioritized tags if they are present.
● Create the opening HTML tag for each tag in the queue and push
it in the stack. Check if the current end Index of the tag is greater than the
previous one in the stack, then create a new tag and
● At the end close all the HTML tags at the given index in the loop.
function
parse
str, markups
){
the string
const
track =
new
Array
(str.length).fill(
null
);
for
let
markup
of
markups) {
const
addAndSort(track, start,
new
295
const
html =
new
Stack();
html.push(
new
Tag(
Number
.MAX_VALUE,
""
));
let
i=
while
){
const
cur = track[i].shift();
cur.text =
`<${cur.tag}>`
in between b.
the parent,
if
const
split =
new
Tag(html.peek().end +
cur.end, cur.tag);
cur.end = html.peek().end;
addAndSort(track, html.peek().end +
, split);
topmost tag
html.peek().text += str[i];
while
(html.peek().end === i) {
html.peek().text +=
`</${html.peek().tag}>`
const
temp = html.pop().text;
html.peek().text += temp;
296
return
html.pop().text;
};
Test Case
Input:
const
encoded = parse(
'Hello, World'
[[
"i"
],
10
,
"u"
],
"b"
],
"i"
],
9
,
"u"
]]);
console
.log(encoded);
Output:
"<i>He<i>l</i></i><i>l<b>o,
<u><u>W</u></u></b></i><b><u><u>or</u></u></b><u>l</u>d"
"
Hello,
OceanofPDF.com
or
l
d"
Using DOMParser()
DOMParser() parses the HTML source code from the string and the
Thus all we have to do is traverse the styles and add tags at the
Then pass this string to the DOMParser() and it will generate the
function
parse
string, markups
){
indexes
const
fragments = markups.reduce((
chars, [start,
end, tag]
) => {
chars[start] =
`<${tag}>`
+ chars[start];
chars[end] +=
``
297
return
chars;
}, [...string]);
// to convert it to HTML
return
new
DOMParser()
.parseFromString(fragments.join(
''
),
'text/html'
.body.innerHTML;
Test Case
Input:
const
encoded = parse(
'Hello, World'
[[
"i"
],
[
10
"u"
],
"b"
],
"i"
],
"u"
]]);
console
.log(encoded);
Output:
"<i>He<i>l</i>l<b>o,
<u><u>W</u></u></b></i><b><u><u>or</u></u></b><u>l</u>d"
"
Hello,
OceanofPDF.com
or
l
d"
298
Problem Statement -
Given a root node and target node, generate a CSS selector from the
Example
Input:
<div id=
"root"
>
<article>
</article>
<section>
on
<p>
<span>
Learnersbucket
<button>
click me!
</button>
<button id=
"target"
>
click me!
</button>
</span>
</p>
</section>
</div>
Output:
> button:nth-child(2)"
To generate the CSS selector, all we have to do is start from the target and
trace back to the root (parent).
● Use a while loop and keep iterating till we have found the root of the
target.
● At the end, add the roots tag name. The selector will begin from this.
299
const
generateSelector = (
root, target
) => {
const
selectors = [];
while
nthChild =
Array
.from(target.parentNode.children).indexOf(target)
const
selector =
`${target.tagName.toLowerCase()}:nth-child(${nthChild})`
selectors.unshift(selector);
target = target.parentNode;
// id is used here
selectors.unshift(
`${target.tagName.toLowerCase()}[id="${target.id}"]`
);
return
selectors.join(
'>'
);
Test Case
Input:
<div id=
"root"
>
<article>
</article>
<section>
on
<p>
<span>
Learnersbucket
<button>
click me!
</button>
<button id=
"target"
>
click me!
</button>
</span>
300
</p>
</section>
</div>
const
root =
document
.getElementById(
"root"
);
const
target =
document
.getElementById(
"target"
);
console
.log(generateSelector(root, target));
Output:
> button:nth-child(2)"
301
Problem Statement -
Given multiple input elements with different names and values inside a
wrapper. Aggregate the values of the input on the names.
Example
Input:
<form id="parent">
<input type="text" name="a.c" value="1"/>
</form>
Output:
"a": {
"c": "1",
"b": {
"d": "2",
"e": "3"
The approach is simple to solve this, create a function that will accept the
parent’s id as input and get all the input elements under that
parent.
Now using Array.reduce() we can aggregate the values of each input on its
key. All we have to do is,
● Split the name on the dot and get all the keys as an array.
● Check if the key already exists or not, if it does not create an empty
object and assign it to it.
● If the key is the last from the array then assign the value to it.
302
● At the end update the reference of this new object to the current object.
const
aggregateValues = (
id
) => {
const
element =
document
.querySelector(
`#${id}`
);
const
inputs = element.querySelectorAll(
'input[type="text"]'
);
return
Array
.from(inputs).reduce((
prev, current
=> {
const
names = current.name.split(
"."
);
let
temp = prev;
// iterate each key in the name
names.forEach((
name, index
) => {
if
(!(name
in
temp)) {
temp[name] = {};
if
(index == names.length -
){
temp[name] = current.value;
}
// reference the next value to current
temp = temp[name];
});
return
prev;
303
}, {});
Test Case
Input:
<form id="parent">
</form>
console.log(aggregateValues('parent'));
Output:
{
"a": {
"c": "1",
"b": {
"d": "2",
"e": "3"
304
Problem Statement -
Example
const
requestInterceptor = (
requestArguments
) => {
console
.log(
"Before request"
);
const
responseInterceptor = (
response
) => {
console
.log(
"After response"
);
fetch(
'https://jsonplaceholder.typicode.com/todos/1'
.then(
response
=> response.json())
.then(
json
=>
console
.log(json))
// "Before request"
// "After response"
/*
"userId": 1,
"id": 1,
"completed": false
*/
Axios, one of the most popular libraries for making network calls,
received.
305
For example, you can add a response interceptor and monitor if the server is
giving 401 unauthorized , then log out the user from the
Before each request, pass the arguments to requestInterceptor and get the
updated value from it, passing it further to the original fetch
method.
const
originalFetch =
window
.fetch;
// request interceptor
window
.requestInterceptor = (
args
) => {
return
args;
// response interceptor
window
.responseInterceptor = (
response
) => {
return
response;
window
.fetch =
async
(...args) => {
306
// request interceptor
args = requestInterceptor(args);
let
response =
await
originalFetch(...args);
// response interceptor
response = responseInterceptor(response);
return
response;
};
Test Case
As you can see in the requestInterceptor we are manually setting the page
number and in the responseInterceptor we are returning the
Input:
// request interceptor
window
.requestInterceptor = (
args
) => {
args[
] = args[
]+
"2"
return
args;
}
// response interceptor
window
.responseInterceptor = (
response
) => {
return
response.json();
fetch(
'https://jsonplaceholder.typicode.com/todos/'
.then(
json
=>
console
.log(json));
© JavaScript Interview Guide | learnersbucket.com
307
Output:
"userId"
"id"
"title"
"completed"
false
}
© JavaScript Interview Guide | learnersbucket.com
308
Problem Statement -
the given amount of time. If a new call is made between that time, the
response from the cache will be returned, else a fresh API call will be made.
Example
const
call = cachedApiCall(
1500
);
// first call
call(
'https://jsonplaceholder.typicode.com/todos/1'
{}).then((
) =>
console
.log(a));
/*
"userId": 1,
"id": 1,
"completed": false
*/
// it will be quick
setTimeout(
()
=> {
call(
'https://jsonplaceholder.typicode.com/todos/1'
{}).then((
a
) =>
console
.log(a));
},
700
);
/*
"userId": 1,
"id": 1,
"completed": false
*/
309
setTimeout(
()
=> {
call(
'https://jsonplaceholder.typicode.com/todos/1'
{}).then((
) =>
console
.log(a));
},
2000
);
/*
"userId": 1,
"id": 1,
"completed": false
}
*/
function will accept the time and return an async inner function that will
accept the arguments to make the API call.
In the inner function, we will create a new unique key from the
Using this key, get the entry from the cache. If there is no entry
present or the time of the entry is expired, make a new API call. Else return
the value of the entry.
To generate the key and make the API call we will be using two helper
functions.
const
generateKey = (
path, config
) => {
const
key =
Object
.keys(config)
.sort((
a, b
) => a.localeCompare(b))
.map((
) => k +
":"
+ config[k].toString())
310
.join(
"&"
);
return
path + key;
};
const
makeApiCall =
async
try
let
response =
await
fetch(path, config);
response =
await
response.json();
return
response;
catch
(e){
console
.log(
"error "
+ e);
}
return
null
};
const
cachedApiCall = (
time
) => {
// to cache data
const
cache = {};
return
async
function
path, config = {}
){
// get the key
const
let
entry = cache[key];
if
(!entry ||
Date
console
.log(
);
try
{
const
value =
await
makeApiCall(path, config)
Date
.now()
+ time };
311
catch
(e){
console
.log(error);
return
cache[key].value;
}
};
Test Case
const
call = cachedApiCall(
1500
);
// first call
call(
'https://jsonplaceholder.typicode.com/todos/1'
{}).then((
) =>
console
.log(a));
/*
{
"userId": 1,
"id": 1,
"completed": false
*/
// it will be quick
setTimeout(
()
=> {
call(
'https://jsonplaceholder.typicode.com/todos/1'
{}).then((
) =>
console
.log(a));
},
700
);
/*
"userId": 1,
"id": 1,
"completed": false
*/
312
setTimeout(
()
=> {
call(
'https://jsonplaceholder.typicode.com/todos/1'
,
{}).then((
) =>
console
.log(a));
},
2000
);
/*
"userId": 1,
"id": 1,
"completed": false
*/
313
Write a custom function to find all the elements with the given class in the
DOM.
Example
Input:
<div class=
'a'
id=
"root"
>
<div class=
'b'
id=
'b-1'
>
<div class=
'a'
id=
'a-2'
>
<div class=
'd'
id=
'd-1'
></div>
</div>
<div class=
'c'
id=
'c-1'
>
<div class=
'a'
id=
'a-3'
>
<div class=
'd'
id=
'd-2'
></div>
</div>
</div>
</div>
</div>
findByClass(
'a'
);
Output:
<div class=
"a"
id=
"root"
>
<div class=
"b"
id=
"b-1"
>
<div class=
"a"
id=
"a-2"
>
<div class=
"d"
id=
"d-1"
></div>
</div>
<div class=
"c"
id=
"c-1"
>
<div class=
"a"
id=
"a-3"
>
<div class=
"d"
id=
"d-2"
></div>
</div>
</div>
</div>
</div>
<div
class
"a"
id=
"a-2"
>
<div class=
"d"
id=
"d-1"
></div>
<
/div>,
314
div>
</div>
element can have multiple classes, all we have to do is start from the body
or the root element and check if the current node’s classlist has the class or
not.
If it has it, then push the node in the result. After that traverse all the
children of the node and for each child check if they have the class in their
classList or not.
We can recursively call the same function for each child and it will call their
children and so on and perform the test and return the result.
function
findByClass
class
){
const
root =
document
.body;
function
search
node
){
let
result = [];
if
(node.classList.contains(
class
)) {
result.push(node);
present
for
const
element
of
node.children) {
// recursively search
const
res = search(element);
© JavaScript Interview Guide | learnersbucket.com
315
result = result.concat(res);
return
result;
return
search(root);
Test Case
Input:
<div class=
'a'
id=
"root"
>
<div class=
'b'
id=
'b-1'
>
<div class=
'a'
id=
'a-2'
>
<div class=
'd'
id=
'd-1'
></div>
</div>
<div class=
'c'
id=
'c-1'
>
<div class=
'a'
id=
'a-3'
>
<div class=
'd'
id=
'd-2'
></div>
</div>
</div>
</div>
</div>
console
.log(findByClass(
'a'
));
Output:
<div class=
"a"
id=
"root"
>
<div class=
"b"
id=
"b-1"
>
<div class=
"a"
id=
"a-2"
>
<div class=
"d"
id=
"d-1"
></div>
</div>
<div class=
"c"
id=
"c-1"
>
<div class=
"a"
id=
"a-3"
>
<div class=
"d"
id=
"d-2"
></div>
</div>
</div>
</div>
</div>
<div
class
"a"
id=
"a-2"
>
<div class=
"d"
id=
"d-1"
></div>
<
/div>,
div>
</div>
317
Implement getByClassNameHierarchy()
Problem Statement -
Example
Input:
</div>
</div>
getByClassNameHierarchy("a>b>c");
Output:
[
● If the current class is the last of the path and it is present in the element,
add it to the result as we have reached the required path
318
● In the end, traverse all the children of the current element and if the
element has the current class, recursively traverse its child
with the next class (increase the index tracker by one), else
traverse the child with the first class (index tracker as zero) and
function
getByClassNameHierarchy
element, classNames
const
classList = classNames.split(
'>'
);
const
result = [];
traverseDOM(element, classList,
, result);
result;
// helper function
function
traverseDOM
){
if
(!element) {
return
const
targetClass = classNames[index];
if
&&
element.classList.contains(targetClass)) {
result.push(element);
return
319
for
const
child
of
element.children) {
if
(element.classList.contains(targetClass)) {
result);
child
else
traverseDOM(child, classNames,
, result);
}
Test Case
Input:
<div class=
"a"
id=
"root"
>
<div class=
"b"
id=
"b-1"
>
<div class=
"c"
id=
"c-1"
>
</div>
<div class=
"c"
id=
"c-2"
>
</div>
</div>
<div class=
"b"
id=
"b-2"
>
<div class=
"c"
id=
"c-3"
>
</div>
</div>
</div>
console
.log(getByClassNameHierarchy(
document
.getElementById(
'root'
),
'a>b>c'
));
Output:
<div class=
"c"
id=
"c-1"
></div>
<div
class
"c"
id=
"c-2"
>
</div>
<div
class
"c"
id=
"c-3"
>
</div>
320
Problem Statement -
Write a function to find all the elements with the given color. Here the color
will be provided in any format like, plain text ( white ), HEXA value ( #fff
or #ffffff ), or RGB value ( RGB(255, 255, 255) ).
Example
Input:
<div id=
"root"
>
<span style=
"color:#fff;"
>
</span>
<span style=
"color:#eee;"
>
</span>
<span style=
"color:white;"
>
</span>
<span style=
</span>
</div>
findElementByColor(
document
.getElementById(
'root'
),
);
Output:
<span style=
"color:#fff;"
>
</span>
<span style=
"color:white;"
>
<
/span>,
span>
For this, we can create a function that will temporarily create a new element
and apply the input color as its style, then using the
function
getHexColor
color
){
const
div =
document
.createElement(
'div'
);
321
div.style.color = color;
let
colors =
window
.getComputedStyle(
document
.body.appendChild(div));
colors = colors.color.match(
/\d+/g
).map(
function
){
return
parseInt
(a,
10
); });
document
.body.removeChild(div);
return
(colors.length >=
)?
'#'
+ (((
1
<<
24
+ (colors[
] <<
16
)+
(colors[
] <<
) + colors[
]).toString(
16
).substr(
))
:
false
For finding the elements with the given color value, we will first
convert the input color value to the HEXA using the above function.
Then recursively traverse all the DOM elements and get their color
values and convert them to HEXA and compare it with the input color
value's HEXA.
If they match, push the element to the result array and return it.
function
findElementByColor
element, colorValue
){
const
inputColorHexa = getHexColor(colorValue);
const
result = [];
// helper function to traverse the DOM
const
search = (
element, attrValue
) => {
let
value = element.style[
'color'
];
322
value = getHexColor(value);
if
result.push(element);
}
// recursively search for each child of the element
for
const
child
of
element.children) {
search(child, attrValue);
};
search(element, colorValue);
return
result;
Test Case
Input:
<div id=
"root"
>
<span style=
"color:#fff;"
>
</span>
<span style=
"color:#eee;"
>
</span>
<span style=
"color:white;"
>
</span>
<span style=
>
4
</span>
</div>
console
.log(findElementByColor(
document
.getElementById(
'root'
),
'white'
));
Output:
<span style=
"color:#fff;"
>
</span>
<span style=
"color:white;"
>
<
/span>,
span>
323
Problem Statement -
throttler is passed a number, only executes that number of the tasks and
passes the other tasks into a queue.
Example
Input:
const
task = [
2
,
10
];
const
count =
5
;
throttle(task, count,
2000
);
// [1, 2, 3, 4, 5] //
immediately
throttle(task, count,
2000
);
// [6, 7, 8, 9, 10] //
after 2 seconds
throttle(task, count,
2000
);
// [1, 2, 3, 4, 5] //
after 2 seconds
In each call, 10 new tasks are pushed and only 5 are executed,
What is throttling?
to accept an array of tasks and a count and run the number of tasks the same
as the count.
store the task. In each throttle call, copy all the tasks to the queue and then
run only the count of tasks from the queue and so on in the
consecutive call.
By default, the count will be of the same size as the array of tasks.
324
const
throttle = (
delay = 1000
) => {
let
lastFunc;
let
lastRan;
let
queue = [];
return
function
() {
function
const
context =
this
const
args =
arguments
// run it immediately
if
(!lastRan) {
const
execute = queue.splice(
, count);
callback(execute);
lastRan =
Date
.now();
else
clearTimeout(lastFunc);
// start a new timer
lastFunc = setTimeout(
function
() {
// invoke it
if
((
Date
325
const
execute = queue.splice(
0
, count);
callback(execute);
lastRan =
Date
.now();
}, delay - (
Date
.now() - lastRan));
};
Test Case
Input:
btn.addEventListener(
'click'
, throttle([
,
10
],
(task) => {
console
.log(task);
},
2000
));
Output:
// 1st call
[
3
10
326
327
Decode a string
Problem Statement -
Input:
"2[a2[b]]"
"3[b2[ca]]"
output:
"abbabb"
"bcacabcacabcaca"
● Else if the character is opening square bracket '[' then check if there is a
count number assigned to it or not. If it is then it may
have already been pushed in the first condition so just add the
● If the character is closing square bracket ']' then get the (top element from
stack) count from numStack and substring from
charStack and decode them, then again add the decoded string
328
● In the end create a string from the charStack (which will have decoded
substring) and return it.
let
decodeString = (
str
) => {
let
numStack =
new
Stack();
let
charStack =
new
Stack();
let
decoded =
""
, temp =
""
for
let
i=
let
count =
let
val = str[i];
// push to numStack
if
/^\d+$/
.test(val)){
numStack.push(val);
else
if
(val ===
'['
){
is number
if
/^\d+$/
.test(str[i
-1
])){
charStack.push(str[i]);
}
else
charStack.push(str[i]);
numStack.push(
);
else
if
(val ===
']'
){
// if close bracket
temp =
""
;
count =
329
while
!==
'['
){
if
===
'['
){
charStack.pop();
decoded = temp.repeat(count);
again
for
let
j=
charStack.push(decoded[j]);
decoded =
""
;
}
else
charStack.push(val);
while
(!charStack.isEmpty()){
return
decoded;
330
Test Case
Input:
console
.log(decodeString(
"2[a2[b]]"
));
console
.log(decodeString(
"3[b2[ca]]"
));
Output:
"abbabb"
"bcacabcacabcaca"
331
A Trie, also known as a digital tree or prefix tree, is a kind of search tree —
an ordered tree data structure used to store a dynamic set or associative
array where the keys are usually strings.
The term “Trie” comes from the word retrieval and is usually
pronounced “try”, to separate it from other “tree” structures.
The main idea behind using Tries as a data structure was that they
332
Each Trie has an empty root node, with links (or references) to other nodes
— one for each possible alphabetic value. The shape and the
structure of a trie is always a set of linked nodes, connecting back to an
empty root node.
Thus, the size of a trie is directly correlated to the size of all the possible
values that the Trie could represent.
Base structure
We will need a TrieNode object to insert a new word in the Trie, which
would represent an entirely new Trie.
const
TrieNode =
function
key
){
this
.key = key;
this
.parent =
null
;
this
.children = {};
this
.end =
false
this
.getWord =
function
() {
let
output = [];
let
node =
this
while
(node !==
null
){
output.unshift(node.key);
node = node.parent;
return
output.join(
''
);
};
333
const
Trie =
function
() {
this
.root =
new
TrieNode(
null
);
To insert a new word in the Trie we have to check for two things.
● Check that the word that needs to be added doesn’t already exist in this
trie.
● Next, if we’ve traversed down the branch where this word ought
to live and the words don’t exist yet, we’d insert a value into the
this
.insert =
function
word
){
let
node =
this
.root;
for
let
i=
in children.
if
(!node.children[word[i]]) {
node.children[word[i]] =
new
TrieNode(word[i]);
node.children[word[i]].parent = node;
node = node.children[word[i]];
word.
if
(i == word.length
-1
){
334
node.end =
true
};
Searching a word in the Trie
● For every character in the word. Check to see if character nodes exist in
children.
this
.contains =
function
word
){
let
node =
this
.root;
for
(
let
i=
in children.
if
(node.children[word[i]]) {
of the trie.
node = node.children[word[i]];
else
return
false
}
}
is it a whole word?
return
node.end;
};
335
Find the word starting with given prefix in the Trie To find all the words
with a given prefix, we need to perform two
operations.
this
.find =
function
prefix
){
let
node =
this
.root;
let
output = [];
for
let
i=
if
(node.children[prefix[i]]) {
node = node.children[prefix[i]];
else
output;
findAllWords(node, output);
return
output;
};
node.
const
findAllWords = (
node, arr
) => {
if
(node.end) {
arr.unshift(node.getWord());
}
// iterate through each children, call recursive
findAllWords
for
let
child
in
node.children) {
findAllWords(node.children[child], arr);
336
.remove =
function
word
){
let
root =
this
.root;
if
(!word)
return
const
removeWord = (
node, word
) => {
let
hasChildren =
Object
.keys(node.children).length
>
contain/include
supplied word
if
(hasChildren) {
node.end =
false
else
empty dictionary
node.parent.children = {};
337
return
true
for
let
key
in
node.children) {
removeWord(node.children[key], word)
return
false
};
removeWord(root, word);
};
const
TrieNode =
function
key
){
.key = key;
this
.parent =
null
this
.children = {};
this
.end =
false
this
.getWord =
function
() {
let
output = [];
let
node =
this
while
(node !==
null
){
output.unshift(node.key);
node = node.parent;
return
output.join(
''
);
};
338
}
const
Trie =
function
() {
this
.root =
new
TrieNode(
null
);
this
.insert =
function
word
){
let
node =
this
.root;
for
let
i=
in children.
if
(!node.children[word[i]]) {
node.children[word[i]] =
new
TrieNode(word[i]);
node.
node.children[word[i]].parent = node;
}
node = node.children[word[i]];
word.
if
(i == word.length
-1
){
node.end =
true
};
this
.contains =
function
(
word
){
let
node =
this
.root;
for
let
i=
in children.
if
(node.children[word[i]]) {
of the trie.
node = node.children[word[i]];
else
339
return
false
is it a whole word?
return
node.end;
};
this
.find =
function
prefix
){
let
node =
this
.root;
let
output = [];
for
let
i=
if
(node.children[prefix[i]]) {
node = node.children[prefix[i]];
else
return
output;
findAllWords(node, output);
return
output;
};
node.
const
findAllWords = (
node, arr
) => {
if
(node.end) {
arr.unshift(node.getWord());
findAllWords
for
let
child
in
node.children) {
findAllWords(node.children[child], arr);
340
.remove =
function
word
){
let
root =
this
.root;
if
(!word)
return
const
removeWord = (
node, word
) => {
let
hasChildren =
Object
.keys(node.children).length
>
contain/include
supplied word
if
(hasChildren) {
node.end =
false
else
empty dictionary
node.parent.children = {};
return
true
for
let
key
in
node.children) {
removeWord(node.children[key], word)
return
false
};
removeWord(root, word);
341
};
Test Case
Input:
const
trie =
new
Trie();
trie.insert(
"peter"
);
trie.insert(
"piper"
);
trie.insert(
"picked"
);
trie.insert(
"pickled"
);
trie.insert(
"pepper"
);
console
.log(trie.contains(
"picked"
));
console
.log(trie.contains(
"pepper"
));
trie.remove(
"pepper"
);
console
.log(trie.find(
"pi"
));
console
.log(trie.find(
"pe"
));
Output:
true
true
"pickled"
,
"picked"
"piper"
"peter"
342
Problem Statement -
different algorithms which will find the first and last occurrence of the
given element.
Example
Input:
,
2
Output:
4
search .
We will be using a modified version of binary search to find the first or last
occurrence of the element in the array.
First Occurence
const
first = (
arr, value
) => {
let
low =
let
high = arr.length -
let
result =
-1
343
while
const
mid =
Math
.ceil((low + high) /
);
occurrence
if
result = mid;
high = mid -
else
if
high = mid -
else
{
range
low = mid +
return
result;
Test Case
Input:
const
arr = [
2
,
10
];
console
.log(first(arr,
));
console
.log(first(arr,
));
Output:
Last Occurence
Just like finding the first occurrence to find the last occurrence we will have
to keep looking for the element in the upper range after it is
344
const
last = (
arr, value
) => {
let
low =
let
high = arr.length -
let
result =
-1
while
const
mid =
Math
.ceil((low + high) /
);
upper range
if
result = mid;
low = mid +
else
if
range
high = mid -
1
else
range
low = mid +
return
result;
Test Case
Ìnput:
const
arr = [
8
,
10
];
console
.log(last(arr,
));
console
.log(last(arr,
));
Output:
345
346
Piping function in JavaScript – Part 1
Problem Statement -
through all the functions in the input object and return the computed value.
Example
Input:
a:{
b:(
a,b,c
) => a+b+c,
c:(
a,b,c
) => a+b-c,
},
d:(
a,b,c
) => a-b-c
Fn(obj)(
1
);
Output:
a:{
b:
c:
},
d:
-1
We can solve this by forming a closure , creating a function that will accept
the object as input, and from this function returning another function that
will accept the arguments.
Inside the inner function. Iterate all the keys of the object, if the value of the
key is a function, pass the arguments to it and store its
computed value on that key. Else recursively call the same function for
nested processing.
347
At the end return the original input object as we are doing the processing in-
place.
const
pipe = (
obj
) => {
the args
return
function
...args
){
for
(
let
key
in
obj) {
let
val = obj[key];
if
typeof
val ===
'function'
){
obj[key] = val(...args);
else
{
processed
obj[key] = pipe(val)(...args);
return
obj;
};
Test Case
Input:
let
test = {
a: {
b: (
a, b, c
) => a + b + c,
c: (
a, b, c
) => a + b - c,
},
d: (
a, b, c
) => a - b - c,
e:
f:
true
};
348
console
.log(pipe(test)(
1
,
));
Output:
"a"
:{
"b"
"c"
},
"d"
-1
"e"
:
"f"
true
349
Problem Statement -
output.
Example
Input:
const
val = { salary:
10000
};
const
getSalary = (
person
) => person.salary
const
addBonus = (
netSalary
) => netSalary +
1000
const
deductTax = (
grossSalary
.3
);
const
result = pipe(
getSalary,
addBonus,
deductTax
)(val);
Output:
7700
Create two functions, in the first function accept all the functions as
arguments and inside it create another function and return it. The
inner function will accept the value and it will pass the value through the
functions that are received in the outer function in the sequence.
At the end return the final output from the last function.
const
pipe =
function
...fns
){
350
function
val
){
for
let
of
fns){
val = f(val);
return
val;
};
};
Test Case
Input:
const
getSalary = (
person
) => person.salary
const
addBonus = (
netSalary
) => netSalary +
1000
const
deductTax = (
grossSalary
.3
);
const
val = { salary:
10000
};
const
result = pipe(
getSalary,
addBonus,
deductTax
)({ salary:
10000
});
console
.log(result);
Output:
7700
351
Problem Statement -
Implement an analytics SDK that exposes log events, it takes in events and
queues them and then starts sending the events.
● Send each event after a delay of 1 second and this logging fails every n %
5 times.
● Send the next event only after the previous one resolves.
Example
Input:
const
sdk =
new
SDK();
sdk.logEvent(
"event 1"
);
sdk.logEvent(
"event 2"
);
sdk.logEvent(
"event 3"
);
sdk.logEvent(
"event 4"
);
sdk.logEvent(
"event 5"
);
sdk.logEvent(
"event 6"
);
sdk.logEvent(
"event 7"
);
sdk.logEvent(
"event 8"
);
sdk.logEvent(
"event 9"
);
sdk.logEvent(
"event 10"
);
sdk.send();
Output:
"-----------------------"
"-----------------------"
OceanofPDF.com
"Analytics sent event 5"
352
"-----------------------"
"-----------------------"
Delay function
The most important part of this function is that the events will be sent after a
delay of 1 second and fails n%5 times.
Create a new Promise and inside that run a setTimeout that will
wait =
()
=>
new
Promise
((
resolve, reject
) => {
setTimeout(
()
=> {
if
this
.count %
5
===
){
reject();
else
resolve();
},
1000
);
});
353
We have to store the events so that they can be sent one by one. We
also need a tracker so that we can reject for each n%5 th call .
class
SDK
constructor
(){
this
.queue = [];
this
.count =
logEvent(ev) {
this
.queue.push(ev);
This will be an async function and in each call, get the first element from the
queue and try the wait() , if it resolves then print the log or perform any
other operation, else if it fails, push the event back in the queue for retry.
Finally, recursively call the same function for the next operation.
Add a base case to stop the execution if there are no more events in the
queue. Also, track the count in each call.
// to send analytics
354
sendAnalytics =
async
function
(){
// stop execution
if
this
.queue.length ===
0
){
return
const
current =
this
.queue.shift();
try
// delay
await
this
.wait();
console
.log(
+ current);
this
.count++;
catch
(e){
// if execution fails
console
.log(
"-----------------------"
);
console
.log(
+ current);
console
.log(
"Retrying sending "
+ current);
console
.log(
"-----------------------"
);
this
.count =
this
.queue.unshift(current);
finally
this
.sendAnalytics();
355
send =
async
function
(){
this
.sendAnalytics();
class
SDK
constructor
(){
.queue = [];
this
.count =
logEvent(ev) {
this
.queue.push(ev);
wait =
()
=>
new
Promise
((
resolve, reject
) => {
setTimeout(
()
=> {
if
this
.count %
===
){
reject();
else
resolve();
}
},
1000
);
});
// to send analytics
sendAnalytics =
async
function
(){
// stop execution
if
this
.queue.length ===
){
return
;
}
356
const
current =
this
.queue.shift();
try
// delay
await
this
.wait();
console
.log(
this
.count++;
catch
(e){
// if execution fails
console
.log(
"-----------------------"
);
console
.log(
+ current);
console
.log(
+ current);
console
.log(
"-----------------------"
);
this
.count =
this
.queue.unshift(current);
finally
this
.sendAnalytics();
}
}
send =
async
function
(){
this
.sendAnalytics();
Test Case
Input:
357
const
sdk =
new
SDK();
sdk.logEvent(
"event 1"
);
sdk.logEvent(
"event 2"
);
sdk.logEvent(
"event 3"
);
sdk.logEvent(
"event 4"
);
sdk.logEvent(
"event 5"
);
sdk.logEvent(
"event 6"
);
sdk.logEvent(
"event 7"
);
sdk.logEvent(
"event 8"
);
sdk.logEvent(
"event 9"
);
sdk.logEvent(
"event 10"
);
sdk.logEvent(
"event 11"
);
sdk.logEvent(
"event 12"
);
sdk.logEvent(
"event 13"
);
sdk.logEvent(
"event 14"
);
sdk.logEvent(
"event 15"
);
sdk.logEvent(
"event 16"
);
sdk.logEvent(
"event 17"
);
sdk.logEvent(
"event 18"
);
sdk.logEvent(
"event 19"
);
sdk.logEvent(
"event 20"
);
sdk.send();
Output:
"Analytics sent event 1"
"-----------------------"
"-----------------------"
"-----------------------"
358
"-----------------------"
"-----------------------"
"-----------------------"
"-----------------------"
"-----------------------"
359
Check if given binary tree is full
Problem Statement -
A full B-tree is defined as a binary tree in which all nodes have either zero or
two child nodes.
Alternatively we can say there is no node in a full B-tree, which has only one
child node.
Example
Input :
/\
/\
Output : Yes
Input :
/\
2
3
Output :No
Recursive Approach
● Create a function which recursively calls itself to check if each node has 0
or 2 children or not.
360
const
isFullTreeRecursive = (
root
) => {
// if empty tree
if
(root ==
null
){
return
true
// if leaf node
if
(root.left ===
null
null
){
return
true
if
((root.left !==
null
null
))
return
(isFullTreeRecursive(root.left) &&
isFullTreeRecursive(root.right));
// if none work
return
false
Test Case
Input:
function
Node
val
){
this
.val = val;
this
.left =
null
this
.right =
null
let
tree =
new
Node(
10
);
tree.left =
new
Node(
20
);
tree.right =
new
Node(
30
);
tree.left.right =
new
Node(
40
);
console
.log(isFullTreeRecursive(tree));
Output:
false
361
Iterative Approach
We can implement the above recursive solution iteratively using a
queue.
● In each iteration get the first value from the queue and check if it has 0 or
2 children then continue. Otherwise return false.
● Add those child’s back to the queue to check their children and so on.
const
isFullTreeIterative = (
root
) => {
if
(root ===
null
){
return
true
const
q = [];
//Add the root to the queue initially
q.unshift(root);
while
(q.length){
const
node = q.pop();
if
(node.left ===
null
null
){
continue
if
(node.left ===
null
|| node.right ===
null
return
false
362
q.unshift(node.left);
q.unshift(node.right);
true
Test Case
Input:
function
Node
val
){
this
.val = val;
this
.left =
null
this
.right =
null
;
let
tree =
new
Node(
10
);
tree.left =
new
Node(
20
);
tree.right =
new
Node(
30
);
tree.left.right =
new
Node(
40
);
tree.left.left =
new
Node(
50
);
console
.log(isFullTreeIterative(tree));
Output:
true
363
Problem Statement -
Given a binary tree, write two programs to find each height and width of it.
Example
Input :
1
/\
/\/\
567
Output :
Height = 3
Width = 4
To find out the depth of the tree we will have to perform the following
operations.
We will recursively find out the depth of the left subtree and right subtree.
Then get the max of both depths and return it.
const
btHeight = (
node
) => {
// if null return 0
if
(node ===
null
){
return
const
leftHeight = btHeight(node.left);
364
const
rightHeight = btHeight(node.right);
return
: rightHeight +
1
Test Case
Input:
function
Node
val
){
this
.val = val;
this
.left =
null
this
.right =
null
;
}
let
tree =
new
Node(
10
);
tree.left =
new
Node(
20
);
tree.right =
new
Node(
30
);
tree.left.right =
new
Node(
40
);
tree.left.left =
new
Node(
50
);
tree.right.right =
new
Node(
70
);
tree.right.left =
new
Node(
60
);
console
.log(btHeight(tree));
Output:
3
The width of a binary tree is the number of nodes present at the given level.
We will first find the height of the tree and then find the width by recursively
checking the number of nodes at every level. Then we will return the
maximum width of them.
365
const
helper = (
node, level
) => {
// if null return 0
if
(node ===
null
){
return
}
// if at root level return 1
if
(level ===
return
if
(level >
){
return
helper(node.left, level -
) + helper(node.right,
level -
);
};
return
};
const
btWidth = (
node
) => {
let
maxWidth =
let
for
let
i=
if
maxWidth = width;
return
maxWidth;
};
Test Case
Input:
function
Node
val
){
this
.val = val;
this
.left =
null
this
.right =
null
366
let
tree =
new
Node(
10
);
tree.left =
new
Node(
20
);
tree.right =
new
Node(
30
);
tree.left.right =
new
Node(
40
);
tree.left.left =
new
Node(
50
);
tree.right.right =
new
Node(
70
);
tree.right.left =
new
Node(
60
);
console
.log(btWidth(tree));
Output:
367
Problem Statement -
Write a simple polyfill for the extends method. Create a simple function that
will accept the two functions parent and child and extend the child function
to have all parent properties.
Example
function
Parent
() {
this
.name =
"abc"
};
Parent.prototype.walk =
function
(){
console
.log (
this
.name +
', I am walking!'
);
};
function
Child
() {
this
.name =
"pqr"
};
Child.prototype.sayHello =
function
(){
console
.log(
'hi, I am a student'
);
};
// function to extend
extend(Parent, Child);
const
child =
new
Child();
child.sayHello();
child.walk();
console
.log(child
instanceof
Parent);
console
.log(child
instanceof
Child);
Output:
"hi, I am a student"
"pqr, I am walking!"
true
true
368
To extend a child from a parent, we must copy all the static and non-static
methods of the complete prototype chain.
And for this, we can either update the reference of the child’s
Here I have used one method for extending and commented on the
const
myExtends = (
SuperType, SubType
) => {
SubType.prototype.__proto__ = SuperType.prototype;
//ES5: Object.setPrototypeOf(SubType.prototype,
SuperType.prototype);
// static methods;
SubType.__proto__ = SuperType;
itself.
SubType.prototype.constructor = SubType;
//ES5: Object.setPrototypeOf(SubType.prototype,
SubType.prototype);
Test Case
Input:
// Parent
function
Person
() {
this
.name =
"abc"
Person.prototype.walk =
function
(){
console
.log (
this
.name +
', I am walking!'
);
};
369
Person.prototype.sayHello =
function
(){
console
.log (
'hello'
);
};
// static methods
Person.staticSuper =
function
(){
console
.log(
'static'
);
};
// child
function
Student
() {
this
.name =
"pqr"
// sayHello
Student.prototype.sayHello =
function
(){
console
.log(
'hi, I am a student'
);
Student.prototype.sayGoodBye =
function
(){
console
.log(
'goodBye'
);
const
const
student1 =
new
Student();
student1.sayHello();
student1.walk();
student1.sayGoodBye();
Student.staticSuper();
console
.log(student1.name);
// check inheritance
console
.log(student1
instanceof
Person);
console
.log(student1
instanceof
Student);
Output:
"hi, I am a student"
"pqr, I am walking!"
370
"goodBye"
"static"
"pqr"
true
true
371
Problem Statement -
seconds.
done animating.
const
loadingBar =
document
.createElement(
"div"
);
let
styleSheet =
null
const
dynamicAnimation = (
name, styles
) => {
//create a stylesheet
if
(!styleSheet) {
styleSheet =
document
.createElement(
"style"
);
styleSheet.type =
"text/css"
document
.head.appendChild(styleSheet);
styleSheet.sheet.insertRule(
styleSheet.length
);
};
372
Using this function we can dynamically add the keyframes of the animation
and then apply these animations to any element.
dynamicAnimation(
"loadingBar"
0%{
width: 0%;
100%{
width: 100%;
}`
);
loadingBar.style.height =
"10px"
loadingBar.style.backgroundColor =
"Red"
loadingBar.style.width =
"0"
loadingBar.style.animation =
"loadingBar 3s forwards"
We are done creating the loading bar, just need to add it to the DOM
to animate, for which we will get an entry element and append this
into that.
const
entry =
document
.getElementById(
"entry"
);
entry.appendChild(loadingBar);
const
generateLoadingBar =
()
=> {
//create a div
const
loadingBar =
document
.createElement(
"div"
);
//apply styles
dynamicAnimation(
"loadingBar"
0%{
width: 0%;
100%{
373
width: 100%;
}`
);
loadingBar.style.height =
"10px"
loadingBar.style.backgroundColor =
"Red"
loadingBar.style.width =
"0"
loadingBar.style.marginBottom =
"10px"
loadingBar.style.animation =
"loadingBar 3s forwards"
const
entry =
document
.getElementById(
"entry"
);
entry.appendChild(loadingBar);
};
Start loading bar animation upon a button click.
Create a button and on its click invoke the above function so that it will
generate the loading bar and will be animated once added to the DOM.
document
.getElementById(
"btn"
).addEventListener(
"click"
(e) => {
generateLoadingBar();
});
once and load the next one once the previous animation is
finished.
The last part of this question is to queue the loading bars when the button is
clicked multiple times and animate them sequentially one
after another.
Create a global variable count and increment its value by one every
time the button is clicked, vice-versa decrease its value by one, when a
loading bar is animated.
© JavaScript Interview Guide | learnersbucket.com
374
let
count =
const
updateCount = (
val
) => {
count += val;
document
.getElementById(
"queueCount"
).innerText
= count;
};
For generating the next loading bar from the queue, we will have to
recursively call the same function (which generates the loading bar) when
the animation of the previous loading bar is done.
every time an animation ends on the element, this is also a reason why I
choose CSS animation over JavaScript timers to animate elements.
loadingBar.addEventListener(
"animationend"
, () =>
updateCount(
-1
);
if
(count >
){
generateLoadingBar();
}
});
We will also need to update the code on the button click, invoke the
generateLoadingBar function only when the count is zero as all other
subsequent calls will be invoked recursively.
375
document
.getElementById(
"btn"
).addEventListener(
"click"
(e) => {
//trigger animation
if
(count ===
){
generateLoadingBar();
//update count
updateCount(
);
});
<!DOCTYPE html>
<html lang="en">
<head>
/>
</head>
<body>
<div id="entry"></div>
//create a stylesheet
if (!styleSheet) {
styleSheet = document.createElement("style");
styleSheet.type = "text/css";
document.head.appendChild(styleSheet);
styleSheet.sheet.insertRule(
styleSheet.length
);
376
};
let count = 0;
//function to update the count
count += val;
document.getElementById("queueCount").innerText = count;
};
//apply styles
//animation keyframes
dynamicAnimation(
"loadingBar",
0%{
width: 0%;
100%{
width: 100%;
}`
);
loadingBar.style.height = "10px";
loadingBar.style.backgroundColor = "Red";
loadingBar.style.width = "0";
loadingBar.style.marginBottom = "10px";
entry.appendChild(loadingBar);
loadingBar.addEventListener("animationend", () => {
updateCount(-1);
377
if (count > 0) {
});
//remove listener
};
//trigger animation
if (count === 0) {
generateLoadingBar();
//update count
updateCount(1);
});
</script>
</body>
</html>
378
completed.
other which runs for the duration when the next animation has to be
triggered.
For example, the next animation should trigger when the first loading bar is
50% done, thus let’s say our original loading bar is going to complete 100%
animation in 3 seconds, which means the next
const
generateLoadingBar =
()
=> {
//fragment
const
fragment =
document
.createDocumentFragment();
const
loadingBar =
document
.createElement(
"div"
);
//apply styles
//animation keyframes
dynamicAnimation(
"loadingBar"
0%{
width: 0%;
100%{
width: 100%;
}`
);
loadingBar.style.height =
"10px"
loadingBar.style.backgroundColor =
"Red"
loadingBar.style.width =
"0"
loadingBar.style.marginBottom =
"10px"
loadingBar.style.animation =
"loadingBar 3s forwards"
const
shadowLoadingBar =
document
.createElement(
"div"
);
//apply styles
//animation keyframes
dynamicAnimation(
"shadowLoadingBar"
,
`
0%{
width: 0%;
100%{
width: 50%;
}`
);
shadowLoadingBar.style.height =
"5px"
shadowLoadingBar.style.backgroundColor =
"green"
shadowLoadingBar.style.width =
"0"
shadowLoadingBar.style.marginBottom =
"10px"
;
shadowLoadingBar.style.animation =
"shadowLoadingBar
1.5s forwards"
fragment.appendChild(loadingBar);
fragment.appendChild(shadowLoadingBar);
380
const
entry =
document
.getElementById(
"entry"
);
entry.appendChild(fragment);
shadowLoadingBar.addEventListener(
"animationend"
() => {
updateCount(
-1
);
if
(count >
){
generateLoadingBar();
});
//remove listener
shadowLoadingBar.removeEventListener(
"animationend"
() => {});
};
If you notice, we are creating two loading bars and adding them in
fragments, and hard-coded the duration of the shadow bar 1.5 and
mathematical calculation.
Also, we have kept the background of the shadow bar green and it is
visible currently (just to show the working), but you can hide it.
381
Problem Statement -
Extend the local storage to accept an expiry time and expire the entry after
that time.
Example
myLocalStorage.setItem(
'foo'
,
'bar'
1000
);
// after 2 seconds
console
.log(myLocalStorage.getItem(
'foo'
));
// null
While adding the entry we will accept the expiry date in milliseconds (30
days by default). Set the expiry date to time from the current date along with
the value and store it in the original local storage.
// add an entry
30
60
*
60
1000
let
result = {
data : value
if
(maxAge){
result.expireTime =
Date
.now() + maxAge;
}
// stringify the result
382
window
.localStorage.setItem(key,
JSON
.stringify(result));
Likewise, while getting the value for the given key, check if there is a value
associated with the key, if it exists and is not expired then return the value,
else remove the entry and return null.
getItem(key) {
let
result =
JSON
.parse(
window
.localStorage.getItem(key));
(result){
if
(result.expireTime <=
Date
.now()){
window
.localStorage.removeItem(key);
return
null
return
result.data;
return
null
window
.myLocalStorage = {
getItem(key) {
let
result =
JSON
.parse(
window
.localStorage.getItem(key));
if
(result){
383
if
(result.expireTime <=
Date
.now()){
window
.localStorage.removeItem(key);
return
null
return
result.data;
return
null
},
// add an entry
30
60
60
1000
let
result = {
data : value
if
(maxAge){
// set the expiry
result.expireTime =
Date
.now() + maxAge;
window
.localStorage.setItem(key,
JSON
.stringify(result));
},
removeItem(key) {
window
.localStorage.removeItem(key);
},
clear() {
window
.localStorage.clear();
};
Test Case
Input:
myLocalStorage.setItem(
'foo'
'bar'
1000
);
setTimeout(
()
=> {
console
.log(myLocalStorage.getItem(
'foo'
));
},
1500
);
Output:
null
385
Problem Statement -
Create your custom cookie that is available on the document object with the
only max-age option.
Example
document
.myCookie =
"blog=learnersbucket"
document
.myCookie =
"name=prashant;max-age=1"
;
//
second
console
.log(
document
.myCookie);
// "blog=learnersbucket; name=prashant"
setTimeout(
()
=> {
console
.log(
document
.myCookie);
},
1500
);
// "blog=learnersbucket"
// "name=prashant" is expired after 1 second
custom property "myCookie" , this will give us the addition methods like
get() and set() that could be used behind the scene to make the cookie entry
and return it.
To implement this, we will create a function with a map to persist the entry
of each cookie.
options, in the set() method we will extract the data and separate it on the
key and value and also the options (max-age) and store
● While getting the cookie, get all the entries of the map and for each entry
check if it has expired or not. If it is expired, delete the
386
To parse the input and separate the data and options, we will be using a
helper function.
// key-value pairs
function
parseCookieString
(
str
){
const
';'
);
const
// like max-age
const
options = {};
for
const
option
of
rest) {
const
options[key] = value;
return
key,
value,
options,
// helper function
function
separateKeyValue
str
){
return
str.split(
'='
).map(
=> s.trim());
For the main logic we can extend the document object with
Object.defineProperty() .
387
// enable myCookie
function
useCustomCookie
() {
// of each cookie
const
store =
new
Map
();
Object
.defineProperty(
document
'myCookie'
,{
configurable:
true
get() {
const
cookies = [];
const
time =
Date
.now();
for
(
const
of
store)
if
store.delete(name);
array
else
cookies.push(
`${name}=${value}`
);
}
}
return
cookies.join(
'; '
);
},
set(val) {
const
let
expires =
Infinity
if
(options[
"max-age"
]) {
expires =
Date
.now() +
Number
(options[
"max-age"
])
1000
388
})
};
Test Case
Input:
useCustomCookie();
document
.myCookie =
"blog=learnersbucket"
document
.myCookie =
"name=prashant;max-age=1"
console
.log(
document
.myCookie);
setTimeout(
()
=> {
console
.log(
document
.myCookie);
},
1500
);
Output:
"blog=learnersbucket; name=prashant"
"blog=learnersbucket"
389
Problem Statement -
actions to update the frozen input object . The input object can only be
updated through this function and the returned value is also frozen.
Actions
const
inputArr = [
1
const
outputArr = update(
inputArr, {_push_: [
]}
);
console
.log(outputArr);
// [1,2,3,4,5,6,7]
---
Object
---
const
state = {
a: {
b: {
c:
},
d:
};
const
newState = update(
state,
}}}}
);
390
console
.log(newState);
/*
"a": {
"b": {
"c": 3
},
"d": 2
*/
---
Array
---
const
inputArr = [
const
outputArr = update(
inputArr,
: {_replace_:
10
}}
);
console
.log(outputArr);
// [1,10,3,4]
const
state = {
a: {
b: {
c:
},
d:
};
const
newState = update(
state,
}}}}
);
console
.log(newState);
/*
391
"a": {
"b": {
"c": 1,
"e": 5
},
"d": 2
*/
const
inputArr = {a: { b:
2
}};
const
item
) => item *
}}});
console
.log(outputArr);
/*
"a": {
"b": 4
*/
We can create a helper function that will accept two arguments (data,
action), the action is always an Object.
We can recursively deep traverse the object and in each call check if the
current key is any of the actions then perform the action
accordingly.
Wrap this helper function inside another parent function. As the input object
will freezed, we cannot directly update it, thus we will create a
392
clone of it. Pass the clone for update through the helper function and in the
end freeze the output before returning it back.
// function to deepfreeze
function
deepFreeze
object
){
let
propNames =
Object
.getOwnPropertyNames(object);
for
(
let
name
of
propNames) {
let
value = object[name];
typeof
value ===
"object"
deepFreeze(value) : value;
return
Object
.freeze(object);
};
function
update
inputObj, action
){
const
clone =
JSON
.parse(
JSON
.stringify(inputObj));
function
helper
target, action
){
for
const
[key, value]
of
Object
.entries(action))
switch
(key) {
case
'_push_'
return
[...target, ...value];
case
'_replace_'
return
value;
// merge the values
case
'_merge_'
if
(!(target
instanceof
Object
)) {
throw
Error
"bad merge"
);
393
return
{...target, ...value};
'_transform_'
return
value(target);
default
// if it is an array
if
(target
instanceof
Array
){
// create a copy
const
res = [...target];
res;
// if it is an object
else
return
...target,
};
};
const
deepFreeze(output);
//return it
394
return
output;
};
const
inputArr = [
];
const
outputArr = update(
inputArr, {_push_: [
]}
);
outputArr[
]=
10
console
.log(outputArr);
// [1,2,3,4,5,6,7]
Test Case 2: _replace_
const
state = {
a: {
b: {
c:
},
d:
};
deepFreeze(state);
const
newState = update(
state,
}}}}
);
// as output is frozen
395
newState.a.b.c =
10
console
.log(newState);
/*
"a": {
"b": {
"c": 3
},
"d": 2
*/
Test Case 3: _merge_
const
state = {
a: {
b: {
c:
},
d:
};
deepFreeze(state);
const
newState = update(
state,
}}}}
);
// as output is frozen
newState.a.b.e =
10
console
.log(newState);
/*
"a": {
396
"b": {
"c": 1,
"e": 5
},
"d": 2
}
*/
const
state = {a: { b:
}};
deepFreeze(state);
const
item
) => item *
}}});
// as output is frozen
newState.a.b =
10
;
console
.log(newState);
/*
"a": {
"b": 4
*/
397
Problem Statement -
Example
const
obj = {
a: {
b: {
c:
2
};
// object is frozen
deepFreeze(obj);
const
draft.a.b.c =
draft.a.b.d =
});
console
.log(newState);
/*
{
"a": {
"b": {
"c": 3,
"d": 4
*/
// it cannot be updated
delete
newState.a.b.c;
398
console
.log(newState);
/*
"a": {
"b": {
"c": 3,
"d": 4
*/
To implement this we will first create a clone of the input obj and then pass
this input object to the callback function of produce, for
processing.
function
deepFreeze
object
){
propNames =
Object
.getOwnPropertyNames(object);
for
let
name
of
propNames) {
let
value = object[name];
typeof
value ===
"object"
deepFreeze(value) : value;
}
return
Object
.freeze(object);
399
};
const
deepEqual = (
object1, object2
) => {
const
keys1 =
Object
.keys(object1);
const
keys2 =
Object
.keys(object2);
// if mismatched keys
if
return
false
for
const
key
of
keys1) {
const
val1 = object1[key];
const
val2 = object2[key];
const
areObjects = val1 &&
typeof
val1 ===
"object"
typeof
val2 ===
"object"
// if are objects
if
(areObjects){
if
(!deepEqual(val1, val2)){
return
false
}
// if are not objects
else
if
return
false
return
true
};
function
produce
base, recipe
){
// clone the frozen object
400
let
clone =
JSON
.parse(
JSON
.stringify(base));
recipe(clone);
if
(deepEqual(base, clone)) {
clone = base;
// deep freeze
deepFreeze(clone);
// return the clone
return
clone;
};
Test Case
const
obj = {
a: {
b: {
c:
};
// object is frozen
deepFreeze(obj);
const
draft.a.b.d =
});
console
.log(newState);
/*
401
"a": {
"b": {
"c": 3,
"d": 4
}
*/
// it cannot be updated
delete
newState.a.b.c;
console
.log(newState);
/*
"a": {
"b": {
"c": 3,
"d": 4
*/
402
While you’re throttling your requests, suppose a high-priority API call needs
to be made, how would you manage it? aka. How to make
There are two ways in which we can prioritize the API calls.
Using Request.Priority
It can be one of the following values: low, high, and auto. By default
browsers fetch requests on high priority.
let
articles =
await
fetch(
'/articles'
);
let
recommendation =
await
fetch(
'/articles/recommendation'
{priority:
'low'
});
According to MDN –
stack is empty, but before returning control to the event loop being used by
the user agent to drive the script’s execution environment.
Thus while throttling , the calls will be made after some delay using the
timer functions.
403
let
callback =
()
=> {
fetch(
'https://jsonplaceholder.typicode.com/todos/1'
.then(
response
=> response.json())
.then(
json
=>
console
.log(json))
let
callback2 =
()
=> {
fetch(
'https://jsonplaceholder.typicode.com/todos/2'
.then(
response
=> response.json())
.then(
json
=>
console
.log(json))
let
urgentCallback =
()
=> {
fetch(
'https://jsonplaceholder.typicode.com/todos/3'
.then(
response
=> response.json())
.then(
json
=>
console
.log(json))
console
.log(
);
setTimeout(callback,
);
setTimeout(callback2,
10
);
queueMicrotask(urgentCallback);
console
.log(
);
Output:
"userId"
"id"
"title"
"completed"
false
"userId"
:
"id"
"title"
"completed"
false
404
"userId"
:
1
"id"
"title"
"completed"
false
Note – The calls are made in priority, but the time at which they get resolved
can vary, thus we can see random outputs.
405
Problem Statement -
DOM.
Example
Input:
const
json = {
type:
'div'
props: { id:
'hello'
, class:
"foo"
},
children: [
{type:
'h1'
, children:
'HELLO'
},
{type:
'p'
, children: [{type:
'span'
, props: {class:
"bar"
}, children:
'World'
}] }
};
JSONtoHTML(json);
Output:
<div id=
"hello"
class=
"foo"
>
<h1>
HELLO
</h1>
<p>
<span class=
"bar"
>
World
</span>
</p>
</div>
● If the JSON element is an array of elements, iterate the array, for each
entry in the array, create a new element of the given type,
406
assign all the properties, and if the children are an array of elements,
recursively call the same function to generate the
const
JSONtoHTML = (
json
) => {
// create a fragment
const
fragment =
document
.createDocumentFragment();
if
Array
.isArray(json)){
for
let
entry
of
json){
const
element =
document
.createElement(entry.type);
// if props available
// set them
if
(entry.props){
for
let
key
in
entry.props){
element.setAttribute(key, entry.props[key]);
// if array of children
if
OceanofPDF.com
(
Array
.isArray(entry.children)){
for
let
child
of
entry.children){
element.appendChild(JSONtoHTML(child));
else
element.innerText = entry.children;
fragment.appendChild(element);
else
return
JSONtoHTML([json]);
return
fragment;
};
Test Case
Input:
const
JSON
=[
{
type:
'div'
props: { id:
'hello'
, class:
"foo"
},
children: [
{type:
'h1'
, children:
'HELLO'
},
{type:
'p'
, children: [{type:
'span'
, props: {class:
"bar"
}, children:
'World'
}] }
},
type:
'section'
props: { id:
'hello-2'
, class:
"foo-2"
},
children: [
{type:
'h1'
, children:
'HELLO-2'
},
{type:
'p'
, children: [{type:
'span'
, props: {class:
"bar-2"
}, children:
'World'
}] }
}];
console
.log(JSONtoHTML(json));
Output:
<div id=
"hello"
class=
"foo"
>
<h1>
HELLO
</h1>
<p>
408
<span class=
"bar"
>
World
</span>
</p>
</div>
<section id=
"hello-2"
class
"foo-2"
>
<h1>
HELLO-2
</h1>
<p>
<span class=
"bar-2"
>
World
</span>
<
/p>
</
section>
409
Problem Statement -
Write a function that takes a DOM node as input and converts it to the
JavaScript object. The object should have the type of node, its
Example
Input:
<div id=
"foo"
>
<h1>
Hello
</h1>
<p class=
"bar"
>
<span>
World!
</span>
</p>
</div>
Output:
"props"
:{
"id"
:
"foo"
},
"children"
:[
"children"
"Hello"
"type"
"h1"
},
"props"
:{
"class"
"bar"
},
"children"
:[
"children"
"World!"
"type"
"span"
],
"type"
"p"
],
410
"type"
:
"div"
There are three things that we need to generally take care of to create this
function.
● If the node does not have any children then get its content, otherwise
iterates each child, and calls the same function recursively with them to
generate the object for each.
We will be using a helper function that will give us all the attributes of the
node.
const
getAllAtrributes = (
node
) => {
let
obj = {};
for
let
att, i =
, atts = node.attributes, n =
atts.length; i < n;
i++){
att = atts[i];
obj[att.nodeName] = att.nodeValue;
return
obj;
};
const
HTMLtoJSON = (
node
) => {
const
output = {};
const
type = node.localName;
let
children = node.innerText;
411
if
(node.children.length >
){
children = [];
for
let
child
of
node.children){
children.push(HTMLtoJSON(child));
};
};
const
props = getAllAtrributes(node);
if
Object
.keys(props).length){
output[
'props'
] = props;
output[
'children'
] = children;
output[
'type'
] = type;
return
output;
};
Test Case
Input:
<div id=
"foo"
>
<h1>
Hello
</h1>
<p class=
"bar"
>
<span>
World!
</span>
</p>
</div>
const
node =
document
.getElementById(
"foo"
);
console
.log(HTMLtoJSON(node));
Output:
"props"
:{
"id"
"foo"
},
412
"children"
:[
"children"
"Hello"
"type"
"h1"
},
"props"
:{
"class"
"bar"
},
"children"
:[
{
"children"
"World!"
"type"
"span"
],
"type"
"p"
],
"type"
"div"
413
Concurrent history tracking system
Problem Statement -
can be tracked.
Example
const
historyTracking = HistoryTracking();
historyTracking.registerEntity(
"document"
);
historyTracking.registerService(
"document"
'JavaScript
Ultimate Guide'
);
historyTracking.track(
"document"
,
'JavaScript Ultimate
Guide'
"Problem
1"
);
historyTracking.track(
"document"
'JavaScript Ultimate
Guide'
"Problem 1,
Problem 2"
);
historyTracking.track(
"document"
'JavaScript Ultimate
Guide'
,
"Problem
3"
);
console
.log(historyTracking.getHistory(
"document"
'JavaScript Ultimate
Guide'
));
inside that and each service will have its own historical record.
We will use a map to store the unique entities. Each entity can have
multiple services and its history stored as an object and this object will be
stored as a value next to the entity in the map.
Whenever a new history entry is requested, we will compare it with the last
history, if data is changed, push it into the history.
We will be using the singleton design pattern to make sure that one instance
is used for tracking.
class
HistoryTrackingHelper
constructor
() {
this
.entities =
new
Map
();
registerEntity(entity) {
this
.entities.set(entity, {});
};
const
existingServices =
this
.entities.get(entity);
if
(!existingServices){
this
else
const
[]};
this
.entities.set(entity, merged);
}
const
services =
this
.entities.get(entity);
const
history = services[service];
const
last = history[history.length -
];
if
(!last){
const
serviceWithNewHistory = {...services,
[service]: [newData]};
415
this
.entities.set(entity, serviceWithNewHistory);
else
const
lastStr =
JSON
.stringify(last);
const
newDataStr =
JSON
.stringify(newData);
if
(lastStr !== newDataStr){
const
serviceWithNewHistory = {...services,
[service]: [...history,
newData]};
this
.entities.set(entity, serviceWithNewHistory);
getHistory(entity, service) {
const
services =
this
.entities.get(entity);
return
services[service];
}
// create a single instance of the tracking
const
HistoryTracking = (
function
(){
let
instance;
return
function
(){
if
(!instance){
instance =
new
HistoryTrackingHelper();
return
instance;
};
})();
Test Case
Input:
const
historyTracking = HistoryTracking();
historyTracking.registerEntity(
"document"
);
416
historyTracking.registerService(
"document"
);
historyTracking.track(
"document"
'JavaScript Ultimate
Guide'
,
"Problem
1"
);
historyTracking.track(
"document"
'JavaScript Ultimate
Guide'
"Problem 1,
Problem 2"
);
historyTracking.track(
"document"
'JavaScript Ultimate
Guide'
"Problem
3"
);
console
.log(historyTracking.getHistory(
"document"
'JavaScript Ultimate
Guide'
));
Output:
"Problem 1"
"Problem 3"
417
Example
const
searchEngine =
new
InMemorySearch();
searchEngine.addDocuments(
'Movies'
{name:
'Avenger'
, rating:
8.5
year:
2017
},
{name:
'Black Adam'
, rating:
8.7
year:
2022
},
{name:
, rating:
8.2
, year:
2023
},
{name:
'Black Panther'
, rating:
9.0
, year:
2022
);
console
.log(searchEngine.search(
'Movies'
>
8.5
, {key:
'rating'
, asc:
false
}));
/*
"rating": 9,
"year": 2022
},
"rating": 8.7,
"year": 2022
*/
For searching, we will take the namespace and a callback function that will
filter the value based on the output of this callback function.
418
This function will also accept an option orderBy parameter, that will have
the key and the order on which the search result will be ordered.
class
InMemorySearch
constructor
() {
// to track namespace and its document
this
.entities =
new
Map
();
registerNameSpace(name){
this
.entities.set(name, []);
addDocuments(nameSpace, ...documents){
const
existing =
this
.entities.get(nameSpace);
(!existing){
this
.entities.set(nameSpace, [...documents]);
else
this
const
docs =
this
.entities.get(nameSpace);
// get it filtered
const
filtered = docs.filter((
) => filterFN(e));
// if orderby is requestd
if
(orderByFN){
419
const
return
filtered.sort((
a, b
) => {
if
(asc){
return
a[key] - b[key];
else
return
b[key] - a[key];
});
return
filtered;
};
Test Case
Input:
const
searchEngine =
new
InMemorySearch();
searchEngine.addDocuments(
'Movies'
{name:
'Avenger'
, rating:
8.5
year:
2017
},
{name:
'Black Adam'
, rating:
8.7
year:
2022
},
{name:
8.2
, year:
2023
},
{name:
'Black Panther'
, rating:
9.0
, year:
2022
);
console
.log(searchEngine.search(
'Movies'
>
8.5
, {key:
'rating'
, asc:
false
}));
Output:
/*
"rating": 9,
"year": 2022
},
"rating": 8.7,
"year": 2022
420
]
*/
421
Problem Statement -
Example
const
strArr = [
'Doomsayer'
'Doomguard'
'Doomhamer'
'Bane of Doom'
'Fearsome Doomguard'
,
'Dr. Boom'
'Majordomo'
'Shadowbomber'
'Shadowform'
'Goldshire footman'
];
const
query =
'an'
fuzzySearch(strArr, query);
A string is considered matching if all characters from the search string are
found in the string in the same order.
The most complex part of this function is creating the fuzzy search
algorithm.
From the problem statement, we understand that the order of
characters matters most in the search. Thus we can use the sliding
422
● Use two variables, one to track the characters of the query and another to
track the last searched position in the string.
● Pick the first character from the string, and get its first index in the string
from the last searched position, if the character is
● If any of the characters are not present return false, else return true.
const
fuzzySearch =
function
str, query
){
str = str.toLowerCase();
query = query.toLowerCase();
// current character
let
i=
, lastSearched =
-1
, current = query[i];
while
(current){
// return false
if
))){
return
false
};
current = query[++i];
};
// return true
return
true
};
Filter all the strings of the array through this function and return the list
with the passed ones.
const
search =
function
arr, query
){
return
arr.filter((
e
};
423
Test Case
Input:
const
arr = [
'Doomsayer'
'Doomguard'
'Doomhamer'
'Bane of Doom'
'Fearsome Doomguard'
'Dr. Boom'
,
'Majordomo'
'Shadowbomber'
'Shadowform'
'Goldshire footman'
];
console
.log(search(arr,
'sh'
));
Output:
"Shadowbomber"
"Shadowform"
"Goldshire footman"
]
424
Problem Statement -
Explanation
AbortController() and get its signal property. Pass this signal property as a
parameter to the fetch API and invoke the abort method of the
When abort() is called, the fetch() promise rejects with an AbortError In the
below example, we have created two buttons, download and abort . On the
download click, an API request will be made and on the abort click the
request will be aborted.
HTML:
<div>
<button class=
"download"
>
Download
</button>
<button class=
"abort"
>
Abort
</button>
</div>
JavaScript:
const
controller =
new
AbortController();
const
signal = controller.signal;
425
// get the buttons
const
downloadBtn =
document
.querySelector(
".download"
);
const
abortBtn =
document
.querySelector(
".abort"
);
// download event
downloadBtn.addEventListener(
"click"
, makeCall);
// abort event
abortBtn.addEventListener(
"click"
, () => {
controller.abort();
console
.log(
"Download aborted"
);
});
function
makeCall
() {
fetch(
'https://jsonplaceholder.typicode.com/photos'
{ signal })
.then((
response
) => {
console
.log(
"complete"
, response);
})
.catch((
err
) => {
console
.error(
èrror: ${err.message}`
);
});
};
button, later click on abort, the API call will terminate with the abort error.
426
Problem Statement -
Given a string and array of keywords, highlight the words in the string that
are part of the array of keywords.
Example
const
str =
const
words = [
'Front'
'End'
'JavaScript'
];
highlight(str, words);
you can see in the above example there are two different words Front and
End but they are highlighted together.
1. Check if any of the words in the string is present in the keywords array
then highlight it.
2. Else, break each word into two sub-words and check for each
sub-word if they are present or not, if any part is present highlight them
separately if both the parts are present highlight them together.
function
highlight
str, keywords
){
const
uniqueKeywords =
new
Set
(keywords);
let
words = str.split(
""
);
const
result = words.map(
word
=> {
427
let
output =
''
// highLight it
if
(uniqueKeywords.has(word)) {
output =
`<strong>${word}</strong>`
for
let
i=
const
prefix = word.slice(
,i+
);
const
suffix = word.slice(i +
);
if
output =
`<strong>${prefix}${suffix}</strong>`
break
// highlight it
else
if
output =
`<strong>${prefix}</strong>${suffix}`
}
// else if the only suffix is present
// highlight it
else
if
output =
`${prefix}<strong>${suffix}</strong>`
return
output !==
''
? output : word;
});
result.join(
""
);
428
Test Case
Input:
const
str =
const
words = [
'Front'
'End'
'JavaScript'
];
console
.log(highlight(str, words));
Output:
429
Reactjs Questions
430
usePrevious() hook
usePrevious() hook will take the current value as input and hold it and will
return it whenever it will get a new value. For the initial render, it will
return undefined as there will not be any previous value for it.
To create the usePrevious() hook we will need to use the useRef() and
useEffect() hook together.
useRef()
Between renderings, you can maintain values using the useRef() Hook
which means the value won’t change or be lost when the React
With the useEffect() hook, we can manage the side effects in the
Thus we can create a new reference using useRef() and update its
value inside the useEffect() whenever a new value is provided, at the end
return the reference value.
function
usePrevious
value
){
const
ref = useRef();
useEffect(
()
=> {
ref.current = value;
}, [value]);
in useEffect above)
431
return
ref.current;
current is the default object available on each reference that can be used to
store any value.
Test case
import
from
"react"
const
usePrevious = (
value
) => {
const
ref = useRef();
useEffect(
()
=> {
ref.current = value;
}, [value]);
in useEffect above)
return
ref.current;
};
const
Example =
()
=> {
const
);
const
prevCount = usePrevious(count);
return
<div>
<h1>
</h1>
<button onClick=
{()
=>
setCount(count - 1)}>Decrement
</button>
© JavaScript Interview Guide | learnersbucket.com
432
</div>
);
};
export
default
Example;
433
useIdle() hook
Create an useIdle() hook in React that will return the boolean value
depending upon the active or inactive state of the user after a defined
amount of time.
For this, there are a set of events that we can listen to like mousemove ,
mousedown , keypress , DOMMouseScroll , mousewheel , touchmove ,
MSPointerMove .
Also, we need to handle edge cases where the window or tab is out of focus,
for which we will listen to the focus and blur events.
If any of these events are triggered then set the user to be Active , else if
none of them have happened for a given amount of time then set the
user to be Idle or Inactive .
We will take duration as input for useIdle(delay) for which if the user is not
performing any action then he will be considered as Idle.
Using useRef() we will track a setTimeout that will change status if the user
has not performed any action for the duration received as input, else clear
the timer and start a fresh timeout.
434
import
from
"react"
const
useIdle = (
delay
) => {
const
[isIdle, setIsIdle] = useState(
false
);
const
timeoutId = useRef();
useEffect(
()
=> {
setup();
return
()
=> {
cleanUp();
};
});
const
startTimer =
()
=> {
};
const
resetTimer =
()
=> {
clearTimeout(timeoutId.current);
goActive();
};
const
goInactive =
()
=> {
setIsIdle(
true
);
};
const
goActive =
()
=> {
setIsIdle(
false
);
startTimer();
};
const
setup =
()
=> {
document
.addEventListener(
"mousemove"
, resetTimer,
false
);
document
.addEventListener(
"mousedown"
, resetTimer,
false
);
435
document
.addEventListener(
"keypress"
, resetTimer,
false
);
document
.addEventListener(
"DOMMouseScroll"
, resetTimer,
false
);
document
.addEventListener(
"mousewheel"
, resetTimer,
false
);
document
.addEventListener(
"touchmove"
, resetTimer,
false
);
document
.addEventListener(
"MSPointerMove"
, resetTimer,
false
);
//edge case
.addEventListener(
"blur"
, startTimer,
false
);
window
.addEventListener(
"focus"
, resetTimer,
false
);
};
const
cleanUp =
()
=> {
document
.removeEventListener(
"mousemove"
, resetTimer);
document
.removeEventListener(
"mousedown"
, resetTimer);
document
.removeEventListener(
"keypress"
, resetTimer);
document
.removeEventListener(
"DOMMouseScroll"
resetTimer);
document
.removeEventListener(
"mousewheel"
, resetTimer);
document
.removeEventListener(
"touchmove"
, resetTimer);
document
.removeEventListener(
"MSPointerMove"
resetTimer);
//edge case
window
.removeEventListener(
"blur"
, startTimer);
window
.removeEventListener(
"focus"
, resetTimer);
// memory leak
clearTimeout(timeoutId.current);
};
// return previous value (happens before update
in useEffect above)
return
isIdle;
};
Test Case
Input:
const
Example =
()
=> {
const
isIdle = useIdle(
2000
);
436
return
<div>
<h1>
</h1>
</div>
);
};
Output:
IsIdle:
false
IsIdle:
true
// after 2 seconds
437
useAsync() hook
● state : It will have one of the four values ["idle", "pending", "success",
● error : If the state is error then this will have the error returned from the
asyncFn .
● refetch() : This function can be used to invoke the function again and
refetch data.
Based on the input and output, let’s implement the useAsync() hook.
At the end, we will pass the immediate flag that we took as input to the
useEffect() hook that will trigger the refetch if the value of the immediate
flag changes and is true .
const
useAsync = (
) => {
"error"]
const
[state, setState] = useState({
status:
"idle"
value:
null
438
error:
null
});
called
const
refetch = useCallback(
()
=> {
setState({
status:
"pending"
value:
null
error:
null
});
return
asyncFn()
.then((
response
) => {
setState({
status:
"success"
value: response,
error:
null
});
})
.catch((
error
) => {
setState({
status:
"error"
value:
null
error: error,
});
});
}, [asyncFn]);
useEffect(
()
=> {
if
(immediate) {
refetch();
}, [refetch, immediate]);
// state values
const
439
return
};
Test Case
Input:
const
fakeApiCall =
()
=> {
return
new
Promise
((
resolve, reject
) => {
setTimeout(
()
=> {
const
rnd =
Math
.random() *
10
rnd <=
? resolve(
"Success"
) : reject(
"Error"
);
},
1000
);
});
};
const
Example =
()
=> {
const
);
return
<div>
<div>
Status: {status}
</div>
<div>
Value: {value}
</div>
<div>
error: {error}
</div>
</div>
);
};
Output:
Status: success
Value: Success
error:
440
useDebounce() hook
Also, we will wrap the logic inside the useCallback() to avoid needless re-
renderings as the callback function returns a memoized function
const
useDebounce = (
=> {
const
timerId = useRef();
const
debounce = useCallback(
function
() {
function
let
context =
this
args =
arguments
is true
is: Yes
const
callNow = immediate && !timerId.current;
// base case
to it.
441
clearTimeout(timerId.current);
timerId.current = setTimeout(
function
() {
timeout variable
in 'immediate' mode
timerId.current =
null
if
(!immediate) {
fn.apply(context, args);
}, delay);
if
},
);
return
debounce;
};
Input:
const
Example =
()
=> {
const
print =
()
=> {
console
.log(
"hello"
);
};
const
debounced = useDebounce(print,
500
);
useEffect(
()
=> {
window
.addEventListener(
"mousemove"
, debounced,
false
);
return
()
=> {
window
.removeEventListener(
"mousemove"
, debounced,
false
);
};
});
442
return
<></>
;
};
Output:
"hello" //after 500 millisecond delay when user stops moving mouse Test
Case 2: With an immediate flag
Input:
const
Example =
()
=> {
const
print =
()
=> {
console
.log(
"hello"
);
};
// immediate
const
debounced = useDebounce(print,
500
true
);
useEffect(
()
=> {
window
.addEventListener(
"mousemove"
, debounced,
false
);
return
()
=> {
window
.removeEventListener(
"mousemove"
, debounced,
false
);
};
});
return
<></>
};
Output:
"hello" //immediately only once till the mouse moving is not stopped
"hello" //immediately again once till the mouse moving is not stopped
443
useThrottle() hook
correctly.
There are scenarios where we may invoke functions when it isn’t
If the user spam the click then this will make an API call on each click.
This is not what we want, we want to restrict the number of API calls that
can be made. The other call will be made only after a specified interval of
time.
Create a useThrottle() hook in React with the leading and trailing flag When
leading is enabled the first function will invoke right away and then after
the specified delay, while when trailing is enabled the first function will
invoke after the delay and so on.
Also, we will wrap the logic inside the useCallback() to avoid needless re-
renderings as the callback function returns a memoized function
444
const
useThrottle = (
=> {
const
timerId = useRef();
const
lastArgs = useRef();
const
throttle = useCallback(
function
...args
){
const
const
waitFunc =
()
=> {
// if trailing invoke the function and start
if
fn.apply(
this
, lastArgs.current);
lastArgs.current =
null
else
timerId.current =
null
};
// if leading run it right away
if
fn.apply(
this
, args);
else
lastArgs.current = args;
if
(!timerId.current) {
},
);
return
throttle;
};
445
Input:
const
Example =
()
=> {
const
print =
()
=> {
console
.log(
"hello"
);
};
const
throttled = useThrottle(print,
2500
, { leading:
true
, trailing:
false
});
return
<button onClick=
{throttled}
>
click me
</button>
};
Output:
"hello"
// immediately
"hello"
// after 2500 milliseconds of last call
"hello"
Input:
const
Example =
()
=> {
const
print =
()
=> {
console
.log(
"hello"
);
};
const
throttled = useThrottle(print,
2500
, { leading:
false
, trailing:
true
});
return
<button onClick=
{throttled}
>
click me
</button>
};
Output:
"hello"
"hello"
"hello"
// after 2500 milliseconds of last call
446
useResponsive() hook
Create useResponsive() hook in React that will return the device type
(isMobile, isTablet, isDesktop) depending upon the window width.
upon the device, rather than hiding and showing through CSS we can
For this, we will assign an event listener to the window object and listen to
the resize event on the function onResizeHandler() that will update the state
whenever the user resizes the screen.
Assigning the event listener and removing it are abstracted inside two
functions Setup() and Cleanup() and it is called inside the useEffect() hook,
we have also called onResizeHandler() to initialize the value.
const
useResponsive =
()
=> {
// screen resolutions
const
false
isTablet:
false
isDesktop:
false
});
useEffect(
()
=> {
onResizeHandler();
Setup();
return
()
=> {
// remove the event
Cleanup();
};
}, []);
447
const
onResizeHandler =
()
=> {
const
isMobile =
window
.innerWidth <=
768
const
isTablet =
window
.innerWidth >=
768
&&
window
.innerWidth
<=
990
const
isDesktop =
window
.innerWidth >
990
};
const
debouncedCall = useDebounce(onResizeHandler,
500
);
const
Setup =
()
=> {
window
.addEventListener(
"resize"
, debouncedCall,
false
);
};
const
Cleanup =
()
=> {
window
.removeEventListener(
"resize"
, debouncedCall,
false
);
};
return
state;
};
keeps resizing, rather update once when the user is done resizing.
Test Case
Input:
const
Example =
()
=> {
const
console
.log(isMobile, isTablet, isDesktop);
return
<></>
};
448
Output:
449
useWhyDidYouUpdate() hook
especially this time around when the front end is becoming more and
more complex.
changed that has triggered the re-rendering. Let us see how we can
function
useWhyDidYouUpdate
name, props
){
const
previousProps = useRef();
useEffect(
()
=> {
if
(previousProps.current) {
const
keys =
Object
.keys({ ...previousProps.current,
...props });
// to store what has change
const
changesObj = {};
keys.forEach((
key
) => {
if
typeof
props[key] ===
"object"
&&
typeof
previousProps.current[key] ===
"object"
){
if
(
JSON
.stringify(previousProps.current[key])
!==
JSON
.stringify(props[key])) {
// add to changesObj
450
changesObj[key] = {
from: previousProps.current[key],
to: props[key],
};
else
if
// add to changesObj
changesObj[key] = {
from: previousProps.current[key],
to: props[key],
};
});
if
Object
.keys(changesObj).length) {
console
.log(
name, changesObj);
}
}
previousProps.current = props;
});
Test Case
Input:
import
from
"react"
const
Counter = React.memo((
props
) => {
useWhyDidYouUpdate(
"Counter"
, props);
return
<div style=
{props.style}
>
{props.count}
</div>
});
export
default
function
App
() {
const
);
const
null
);
© JavaScript Interview Guide | learnersbucket.com
451
const
counterStyle = {
fontSize:
"3rem"
color:
"red"
};
return
<div>
<div className=
"counter"
>
<Counter
count=
{count}
style=
{counterStyle}
testCaseWithArray=
{testCase}
function=
{()
=>
console.log(count)}
/>
<button
onClick=
{()
=>
setCount(count + 1);
setTestCase([count + 1]);
}}
>
Increment
</button>
</div>
</div>
);
Output:
count:
from: 0
to: 1
function:
testCaseWithArray:
from: null
to: [1]
452
useOnScreen() hook
Another case is when you want to track the user activity like when a user is
starring a product (product is in the viewport) so that you can use this data
for recommendations.
For this we can create a useOnScreen() hook that will return boolean value
if the component is the view port or not.
2. Using getBoundingClientRect() .
With useRef() we will create reference to the DOM element which we want
to track, and then pass this to the useOnScreen() hook.
useOnScreen() hook will set up the observation for the ref when the
component will be mounted. This will be done in the useEffect() and then
create an instance of IntersectionObserver and if the entry is
otherwise false .
function
useOnScreen
ref
){
453
const
false
);
const
observer =
new
IntersectionObserver(
[entry]
) => {
setIntersecting(entry.isIntersecting);
);
useEffect(
()
=> {
observer.observe(ref.current);
is unmounted
return
()
=> {
observer.disconnect();
};
}, []);
return
isIntersecting;
Test Case
const
Element = (
{ number }
) => {
const
ref = useRef();
const
isVisible = useOnScreen(ref);
return
<div ref=
{ref}
className=
"box"
>
{number}
</div>
);
};
const
DummyComponent =
()
=> {
const
arr = [];
for
let
i=
;i<
20
; i++) {
arr.push(
<Element key=
{i}
number=
{i}
/>
);
454
return arr;
};
Using getBoundingClientRect()
If the top of the element is greater than zero but less than the
Assign a scroll event on the window and inside the listener get the
function
useOnScreen2
ref
){
const
false
);
// determine if the element is visible
const
observer =
function
() {
const
offset =
50
const
top = ref.current.getBoundingClientRect().top;
<=
window
.innerHeight);
};
useEffect(
()
=> {
// first check
observer();
window
.addEventListener(
"scroll"
, observer);
455
return
()
=> {
window
.removeEventListener(
"scroll"
, observer);
};
}, []);
return
isIntersecting;
Test Case
const
Element = (
{ number }
) => {
const
ref = useRef();
const
isVisible = useOnScreen2(ref);
return
<div ref=
{ref}
className=
"box"
>
{number}
{isVisible ? Ì am on screen` : Ì am invisiblè}
</div>
);
};
const
DummyComponent =
()
=> {
const
arr = [];
for
let
i=
;i<
20
; i++) {
arr.push(
<Element key=
{i}
number=
{i}
/>
);
return arr;
};
456
useScript() hook
There are scripts that we don’t require on the initial load of our app, rather
than in certain components or places.
For example, Google Adsense script, we can load it after once the
application is ready and the component that will display the ads is
mounted.
The idea is simple, pass the script source to the useScript() hook and it will
check if any script with this source is already injected or not, if it is present,
return ' ready ' state. Else create a new script with the source and inject it at
the end of the body.
Assign event listeners on this script tag, which will update the statuses.
function
useScript
src
){
"ready", "error")
const
"loading"
"idle"
);
useEffect(
()
=> {
(!src) {
setStatus(
"idle"
);
return
or not
let
script =
document
.querySelector(
`script[src="${src}"]`
);
if
(script) {
457
setStatus(script.getAttribute(
"data-status"
));
else
// create script
script =
document
.createElement(
"script"
);
script.src = src;
script.async =
true
script.setAttribute(
"data-status"
,
"loading"
);
document
.body.appendChild(script);
const
setAttributeFromEvent = (
event
) => {
script.setAttribute(
"data-status"
, event.type
===
"load"
"ready"
"error"
);
};
script.addEventListener(
"load"
, setAttributeFromEvent);
script.addEventListener(
"error"
, setAttributeFromEvent);
const
setStateFromEvent = (
event
) => {
setStatus(event.type ===
"load"
"ready"
:
"error"
);
};
// setup
script.addEventListener(
"load"
, setStateFromEvent);
script.addEventListener(
"error"
, setStateFromEvent);
// clean up
return
()
=> {
if
(script) {
script.removeEventListener(
"load"
, setStateFromEvent);
script.removeEventListener(
"error"
, setStateFromEvent);
};
}, [src]);
return
status;
458
Test Case
const
Dummy =
()
=> {
const
status = useScript(
"https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.
js"
);
console
.log(status);
return
<></>
};
459
useOnClickOutside() hook
Check if the user has clicked outside the component or not. Create the
useOnClickOutside() hook in React that will help us to detect it.
reference and the callback function and will invoke the callback
mousedown , touchstart and on the event fire check if the event.target is not
the descendant of the reference then invoke the callback.
Wrap this logic inside the useEffect() hook so that we can assign and
remove listeners.
function
useOnClickOutside
ref, callback
){
useEffect(
()
=> {
const
listener = (
event
) => {
// return
if
(!ref.current || ref.current.contains(event.target))
return
;
}
callback(event);
};
document
.addEventListener(
"mousedown"
, listener);
document
.addEventListener(
"touchstart"
, listener);
return
()
=> {
document
.removeEventListener(
"mousedown"
listener);
© JavaScript Interview Guide | learnersbucket.com
460
document
.removeEventListener(
"touchstart"
, listener);
};
},
[ref, callback]
);
Test Case
Input:
function
Example
() {
const
ref = useRef();
useOnClickOutside(ref, () => {
console
.log(
"Clicked"
);
});
return
<div>
<p>
</p>
<p ref=
{ref}
>
Click me!
</p>
</div>
);
Output:
"Clicked"
461
useHasFocus() hook
To create the useHasFocus() we will have to listen to the focus and blur
events on the window.
Use useState() to persist the state and useEffect() to assign and remove the
event listeners.
Whenever the window is blurred, set the focus state to false , else whenever
the window is focused, update the state to true .
import
{ useState, useEffect }
from
"react"
const
useHasFocus =
()
=> {
const
document
.hasFocus());
useEffect(
()
=> {
const
onFocus =
()
=> setFocus(
true
);
const
onBlur =
()
=> setFocus(
false
);
window
.addEventListener(
"focus"
, onFocus);
window
.addEventListener(
"blur"
, onBlur);
return
()
=> {
window
.removeEventListener(
"focus"
, onFocus);
window
.removeEventListener(
"blur"
, onBlur);
462
};
}, []);
return
focus;
};
Test Case
Input:
const
Example =
()
=> {
const
focus = useHasFocus();
console
.log(focus);
return
<></>
};
Output:
true
463
useToggle() hook
values and the start index and toggles the next value in the array. If the
index reaches to the last index of the array, reset the index.
This hook will return the current value and the toggle() method using which
we can toggle the values.
To create this hook all we need to do is track the indexes and we can use the
index to return the value from the array thus, use the
useState() hook to persist and track indexes and then use the
import
{ useCallback, useState }
from
"react"
const
useToggle = (
values, startIndex = 0
) => {
const
component,
reset it if it goes
beyond the limit.
const
toggle = useCallback(
()
=> setIndex((
prevIndex
prevIndex +
)),
[values]
);
return
[values[index], toggle];
};
464
Test Case
Input:
function
Example
() {
function
const
"a"
"b"
"c"
"d"
],
);
return
<button onClick=
{toggleValue}
>
"currentValue"
{currentValue}
</button>
export
default
Example;
Output:
currentValue: c
// initially
currentValue: d
// onClick
currentValue: a
// onClick
currentValue: b
// onClick
currentValue: c
// onClick
465
useCopy() hook
Implement an useCopy() hook in React that copies the given text to the
clipboard.
The useCopy() method returns a method copy(text) which accepts the text
as input and copies that text and the copied text.
Both operations are asynchronous and return promises. For our use,
we will use the writeText(text) and wrap this inside the try…catch block.
We will also use the useState() hook to persist the copied text. If the
promise is fulfilled then update the state with the text else set the state to
null.
This operation will take place inside the copy() function that accepts the
text as input and tries to copy that.
import
{ useState }
from
"react"
const
useCopy =
()
=> {
const
const
copy =
async
(text) => {
if
(!navigator?.clipboard) {
console
.warn(
);
466
return
false
state if worked
try
await
navigator.clipboard.writeText(text);
setCopiedText(text);
catch
(error) {
console
.error(
error);
setCopiedText(
null
);
};
return
[copiedText, copy];
OceanofPDF.com
};
Test Case
Input:
function
Example
() {
const
return
<button onClick=
{()
=>
copy("Hello World!")}>
"copiedText" :
{copiedText}
</button>
}
export
default
Example;
Output:
copiedText:
// initially
// after click
467
useLockedBody() hook
Implement the useLockedBody() hook in React that will lock the body from
further scrolling.
The useLockedBody() hook will take the reference of the parent and return
the lock state and the method that will toggle the lock state.
To lock the body, we will have to remove the overflow from the body so that
everything inside it is prevented from scrolling and hide the
scrollbar.
To hide the scroll bar, get the scrollWidth of the referenced element and add
the same size right padding to the body to cover the gap.
This complete processing will take inside the useLayoutEffect() hook as the
side effect is with the DOM.
Use a state to monitor the toggling and depending upon the toggle
state, lock or unlock the body.
const
useLockedBody = (
=> {
const
useLayoutEffect(
()
=> {
if
(!locked) {
return
const
originalOverflow =
document
.body.style.overflow;
const
originalPaddingRight =
document
.body.style.paddingRight;
document
.body.style.overflow =
"hidden"
468
const
root = ref.current;
// or root
const
- root.scrollWidth :
0
if
(scrollBarWidth) {
document
.body.style.paddingRight =
`${scrollBarWidth}px`
// clean up
return
()
=> {
document
.body.style.overflow = originalOverflow;
if
(scrollBarWidth) {
document
.body.style.paddingRight = originalPaddingRight;
}
};
}, [locked]);
useEffect(
()
=> {
if
setLocked(initiallyLocked);
}, [initiallyLocked]);
return
[locked, setLocked];
};
Test Case
Input:
const
Example =
()
=> {
const
ref = useRef();
const
return
<div style=
{{
"abc"
ref=
{ref}
>
<button onClick=
{()
=>
setLocked(!locked)}>{locked
? "unlock scroll"
469
: "lock scroll"}
</button>
</div>
);
};
470
Problem Statement -
//App.js
import
React, { useState }
from
"react"
const
App =
()
=> {
const
);
const
);
const
false
);
basicReset =
()
=> {
setStart(
false
);
};
//store number
const
numberChangeHandler = (
) => {
const
{ value } = e.target;
setNumber(value);
basicReset();
};
//store duration
const
durationChangeHandler = (
) => {
const
{ value } = e.target;
setDuration(value);
basicReset();
};
const
startHandler =
()
=> {
471
};
const
resetHandler =
()
=> {
window
.location.reload();
};
return
<section className="input-area">
<div>
<div>
<input
id="number"
type="number"
value={number}
onChange={numberChangeHandler}
/>
</div>
<div>
<input
id="duration"
type="number"
value={duration}
onChange={durationChangeHandler}
/>
</div>
</div>
<br />
<div>
<button onClick={resetHandler}>reset</button>
</div>
</section>
</main>
);
};
472
Solution 1 : Using setInterval
If you are a straight forward developer like me who likes to try the familiar
approaches, then the first thing that comes to your mind is using a setInterval
function.
Calculate the time interval at which the setInterval should be called in order
to increment the number.
We can do that with this simple formula (duration / number) * 1000 , for
example, (2 / 1000) * 1000 = 2 , which means we have to increment the
counter every 2 milliseconds to reach from 0 to 1000 in 2 seconds.
Now there are two ways in which you can implement this in react,
1. Ref to the DOM element and increment the count directly in each interval
call.
Both of these approaches do not affect the time because all we are
//CountMethods.js
import
React, { useEffect, useState, useRef }
from
"react"
473
//setInterval
const
CountSetInterval = (
props
) => {
const
intervalRef = useRef();
const
countRef = useRef();
// label of counter
// number to increment to
const
const
"0"
);
const
Date
.now());
useEffect(
()
=> {
let
start =
const
end =
parseInt
(number);
// if zero, return
if
return
let
totalMilSecDur =
parseInt
(duration);
let
1000
let
timer = setInterval(
()
=> {
start +=
setCount(
String
(start));
// countRef.current.innerHTML = start;
if
clearInterval(timer);
const
diff =
Date
.now() - timeTaken;
setTimeTaken(diff /
1000
);
// setCount(String(start));
}, incrementTime);
// dependency array
}, [number, duration]);
return
<>
<span ref=
{countRef}
className=
"Count"
>
{count}
</span>
{" "}
{" "}
<span>
| Took :
<b>
{timeTaken}
</b>
seconds to complete
</span>
)}
</>
);
};
I have also added a time log to determine exactly how much time it
takes to increment the count in order to make sure we are progressing in the
right direction.
Let’s call this function on the click of the start button inside the input
function.
// app.js
import
React, { useState }
from
"react"
import
{ CountSetInterval }
from
"./CountMethods"
const
App =
()
=> {
const
);
const
0
);
const
false
);
475
const
basicReset =
()
=> {
setStart(
false
);
};
//store number
const
numberChangeHandler = (
e
) => {
const
{ value } = e.target;
setNumber(value);
basicReset();
};
//store duration
const
durationChangeHandler = (
) => {
const
{ value } = e.target;
setDuration(value);
basicReset();
};
const
startHandler =
()
=> {
// trigger the animation
setStart(
true
);
};
const
resetHandler =
()
=> {
window
.location.reload();
};
return
<section className="input-area">
<div>
<div>
<label>Number:</label>{" "}
<input
type="number"
value={inputValue}
onChange={inputChangeHandler}
/>
</div>
<div>
476
<label>Duration:</label>{" "}
<input
type="number"
value={duration}
onChange={durationChangeHandler}
/>
</div>
</div>
<br />
<div>
<button onClick={resetHandler}>reset</button>
</div>
</section>
<br />
<section className="result-area">
<div>
SetInterval:{" "}
{(start && (
<CountSetInterval
label={"count"}
number={inputValue}
duration={parseInt(duration)}
/>
)) ||
0}
</div>
</section>
</main>
);
};
477
Output
Weird!, right?
Well, it turns out that the setInterval function is not behaving as we have
thought it should.
Let's change the approach and try to implement the same logic using
setTimeout.
//setTimeout
const
CountSetTimeout = (
props
) => {
const
intervalRef = useRef();
const
countRef = useRef();
478
// label of counter
// number to increment to
const
const
"0"
);
// calc time taken for computation
const
Date
.now());
useEffect(
()
=> {
let
start =
const
end =
parseInt
(number);
// if zero, return
if
let
totalMilSecDur =
parseInt
(duration);
let
1000
let
counter =
()
=> {
intervalRef.current = setTimeout(
()
=> {
start +=
setCount(
String
(start));
// countRef.current.innerHTML = start;
counter();
if
clearTimeout(intervalRef.current);
const
diff =
Date
.now() - timeTaken;
// setCount(String(start));
© JavaScript Interview Guide | learnersbucket.com
479
setTimeTaken(diff /
1000
);
}, incrementTime);
};
//invoke
counter();
// dependency array
}, [number, duration]);
return
<>
<span ref=
{countRef}
className=
"Count"
>
{count}
</span>
{" "}
{" "}
<span>
| Took :
<b>
{timeTaken}
</b>
seconds to complete
</span>
)}
</>
);
};
Let us see what happens when we invoke this function on the click of the
start button.
// app.js
import
React, { useState }
from
"react"
import
{ CountSetTimeout }
from
"./CountMethods"
const
App =
()
=> {
const
);
const
);
const
false
);
const
basicReset =
()
=> {
setStart(
false
);
480
};
//store number
const
numberChangeHandler = (
) => {
const
{ value } = e.target;
setNumber(value);
basicReset();
};
//store duration
const
durationChangeHandler = (
) => {
const
{ value } = e.target;
setDuration(value);
basicReset();
};
const
startHandler =
()
=> {
true
);
};
const
resetHandler =
()
=> {
window
.location.reload();
};
return
<section className="input-area">
<div>
<div>
<label>Number:</label>{" "}
<input
type="number"
value={inputValue}
onChange={inputChangeHandler}
/>
</div>
<div>
<label>Duration:</label>{" "}
<input
type="number"
value={duration}
481
onChange={durationChangeHandler}
/>
</div>
</div>
<br />
<div>
<button onClick={resetHandler}>reset</button>
</div>
</section>
<br />
<section className="result-area">
<div>
SetInterval:{" "}
{(start && (
<CountSetTimeout
label={"count"}
number={inputValue}
duration={parseInt(duration)}
/>
)) ||
0}
</div>
</section>
</main>
);
};
Output
482
If you read the definition of each of these methods you will realize that.
Which means the time specified for either of these functions is the
After some research on MDN, I found out that there are two major
1. Clamping.
2.Execution context
The timer can also fire later when the page (or the OS/browser itself) is busy
with other tasks. One important case to note is that the
483
According to MDN –
The window.requestAnimationFrame() method tells the browser that you
wish to perform an animation and requests that the browser calls a
In simple terms what this method does is ask the browser to perform
484
You should call this method whenever you’re ready to update your
animation on screen. This will request that your animation function be called
before the browser performs the next repaint. The number of
callbacks is usually 60 times per second, but will generally match the display
refresh rate in most web browsers as per W3C recommendation.
Now using a startTime variable which stores the time before invoking the
function and this current timestamp which we receive in the
callback every time, we can recursively invoke this function for the given
duration and using a good calculation we can increment the
count.
come with clever calc which will increment the number based on much
For bigger numbers this is not incrementing the count by 1 but still the
animation is happening so fast that human eyes will not be able to
differentiate.
//Animation
const
countAnimate = (
=> {
let
startTime =
null
currentTime variable
let
currentTime =
Date
.now();
const
step = (
currentTime
) => {
time to startTime
if
(!startTime) {
startTime = currentTime;
the number to be
displayed
const
progress =
Math
.min((currentTime - startTime)
/ duration,
);
gotten above
obj.innerHTML =
Math
.floor(progress * (lastVal
- initVal) + initVal);
(lastVal)
if
(progress <
1
){
window
.requestAnimationFrame(step);
else
window
.cancelAnimationFrame(
window
.requestAnimationFrame(step));
const
const
elm =
document
.createElement(
"SPAN"
);
elm.innerHTML =
` | Took : <b>${diff
/ 1000}</
b>
seconds to
completè;
obj.appendChild(elm);
};
//start animating
window.requestAnimationFrame(step);
};
// app.js
486
import
from
"react"
;
import
{ countAnimate }
from
"./CountMethods"
const
App =
()
=> {
const
);
const
);
const
false
);
const
countRef = useRef();
const
basicReset =
()
=> {
setStart(
false
);
countRef.current.innerHTML =
"0"
};
//store number
const
numberChangeHandler = (
) => {
const
{ value } = e.target;
setNumber(value);
basicReset();
};
//store duration
const
durationChangeHandler = (
) => {
const
{ value } = e.target;
setDuration(value);
basicReset();
};
const
startHandler =
()
=> {
true
);
countAnimate(
countRef.current,
parseInt
(inputValue),
parseInt
(duration) *
1000
);
};
const
resetHandler =
()
=> {
window
.location.reload();
© JavaScript Interview Guide | learnersbucket.com
487
};
return
<section className="input-area">
<div>
<div>
<label>Number:</label>{" "}
<input
type="number"
value={inputValue}
onChange={inputChangeHandler}
/>
</div>
<div>
<label>Duration:</label>{" "}
<input
type="number"
value={duration}
onChange={durationChangeHandler}
/>
</div>
</div>
<br />
<div>
<button onClick={resetHandler}>reset</button>
</div>
</section>
<br />
<section className="result-area">
<div>
</div>
</section>
</main>
);
};
export default App;
488
Output
in the browser .
489
Problem Statement -
The question was quoted as “If a user scrolls and sees any property
and stays there for more than 5 sec then call API and store that
property”.
Apart from the online real-estate platform, this can be also applied on other
platforms as well, such as social media like Facebook where if users read a
post for a few seconds, store and use it to provide
Let us see how we should approach such problems and then solve
useOnScreen() hook that will determine if the current element is inside the
viewport or not. If it is under the viewport, make an api call and log it.
const
Elements = (
{ index }
) => {
// element style
const
style = {
flex:
"1 300px"
height:
"300px"
display:
"inline-flex"
490
alignItems:
"center"
justifyContent:
"center"
margin:
"5px"
background:
"red"
fontSize:
"40px"
color:
"#fff"
};
return
<div style=
{style}
>
<div>
{index}
</div>
</div>
);
};
Form an array of blocks from this element and render this inside a
Create a reference to this wrapper element so that we can get all its children
and then check which of them are visible.
const
Example =
()
=> {
const
ref = useRef([]);
const
elementsList = [];
for
let
i=
; i <=
20
; i++) {
elementsList.push(
<Elements index=
{i}
/>
);
// element style
const wrapperStyle = {
display: "flex",
alignItems: "center",
justifyContent: "center",
flexWrap: "wrap",
};
491
return (
<div style=
{wrapperStyle}
ref=
{ref}
>
{elementsList}
</div>
);
};
const
isInViewport =
function
(
elem
){
const
bounding = elem.getBoundingClientRect();
return
bounding.top >=
&&
bounding.left >=
&&
bounding.bottom <= (
window
.innerHeight ||
document
.documentElement.clientHeight) &&
bounding.right <= (
window
.innerWidth ||
document
.documentElement.clientWidth)
);
};
Finally listen to the debounced scroll event after a delay and check what all
elements are visible when the user stops scrolling.
const
makeApiCall =
async
() => {
const
htmlArray = [...ref.current.children];
htmlArray.forEach((
) => {
console
.log(e.innerText, isInViewport(e));
© JavaScript Interview Guide | learnersbucket.com
492
});
};
const
debouncedApiCall = useDebounce(makeApiCall,
5000
);
useEffect(
()
=> {
window
.addEventListener(
"scroll"
, debouncedApiCall);
return
()
=> {
window
.removeEventListener(
"scroll"
, debouncedApiCall);
};
}, []);
import
from
"react"
const
useDebounce = (
=> {
const
timerId = useRef();
// create a memoized debounce
const
debounce = useCallback(
function
() {
function
let
context =
this
args =
arguments
is true
is: Yes
const
to it.
helps to reset
clearTimeout(timerId.current);
timerId.current = setTimeout(
function
() {
timeout variable
in 'immediate' mode
493
timerId.current =
null
if
(!immediate) {
fn.apply(context, args);
}, delay);
if
},
);
return
debounce;
};
function
useOnScreen
(
ref
){
const
false
);
const
observer =
new
IntersectionObserver((
[entry]
=> {
setIntersecting(entry.isIntersecting);
});
useEffect(
()
=> {
// assign the observer
observer.observe(ref.current);
is unmounted
return
()
=> {
observer.disconnect();
};
}, []);
return
isIntersecting;
const
Elements = (
{ index }
) => {
// element style
494
const
style = {
flex:
"1 300px"
height:
"300px"
display:
"inline-flex"
alignItems:
"center"
justifyContent:
"center"
margin:
"5px"
,
background:
"red"
fontSize:
"40px"
color:
"#fff"
};
return
<div style=
{style}
>
<div>
{index}
</div>
</div>
);
};
const
Example =
()
=> {
const
ref = useRef([]);
const
isInViewport =
function
elem
){
const
bounding = elem.getBoundingClientRect();
return
bounding.top >=
0
&&
bounding.left >=
&&
bounding.bottom <= (
window
.innerHeight ||
document
.documentElement.clientHeight) &&
bounding.right <= (
window
.innerWidth ||
document
.documentElement.clientWidth)
);
};
const
makeApiCall =
async
() => {
const
htmlArray = [...ref.current.children];
495
htmlArray.forEach((
) => {
if(isInViewport(e)){
console
.log(e.innerText);
});
};
const
debouncedApiCall = useDebounce(makeApiCall,
5000
);
useEffect(
()
=> {
window
.addEventListener(
"scroll"
, debouncedApiCall);
return
()
=> {
window
.removeEventListener(
"scroll"
, debouncedApiCall);
};
}, []);
elementsList = [];
for
let
i=
; i <=
20
; i++) {
elementsList.push(
<Elements index=
{i}
/>
);
// element style
const wrapperStyle = {
display: "flex",
alignItems: "center",
justifyContent: "center",
flexWrap: "wrap",
};
return (
<div style=
{wrapperStyle}
ref=
{ref}
>
{elementsList}
</div>
);
};
496
Output
497
Implement a hook in React that will return the selected text on the
web page and the coordinates of the selection so that a popup can be shown
to Tweet the selected text just like Medium.
event get the window selection to get the highlighted text and the
current node of the text. The text can be overlapping thus we can get the start
and the end node.
We can pass the element reference to the hook just to restrict the
selection of particular elements. Check if the selected text’s start and end
node is part of the element then only get the selection and its
coordinates.
const
useSelectionText = (
ref
) => {
// and tools
const
false
});
const
onMouseup =
()
=> {
const
selection =
window
.getSelection();
const
startNode = selection.getRangeAt(
).startContainer.parentNode;
const
endNode = selection.getRangeAt(
).endContainer.parentNode;
selection node
498
if
(!startNode.isSameNode(ref.current) ||
!startNode.isSameNode(endNode)) {
setData({
showTools:
false
});
return
const
{ x, y, width } =
selection.getRangeAt(
).getBoundingClientRect();
if
(!width) {
setData({
showTools:
false
});
return
// if text is selected
the selection
if
(selection.toString()) {
setData({
x: x,
y: y +
window
.scrollY -
25
,
showTools:
true
selectedText: selection.toString(),
width,
});
};
// handle selection
useEffect(
()
=> {
document
.addEventListener(
"mouseup"
, onMouseup);
499
// remove the listener
return
()
=> {
document
.removeEventListener(
"mouseup"
, onMouseup);
};
}, []);
return
data;
};
Usage
const
Example =
()
=> {
const
ref = useRef();
const
data = useSelectionText(ref);
return
<div>
{data.showTools && (
<span
style={{
position: "absolute",
top: `${data.y}px`,
width: data.width / 2,
display: "inline-block",
height: "20px",
textAlign: "center",
}}
>
24">
<path
fill="#000000"
500
/>
</svg>
24">
<path
fill="#000000"
d="M18.5,1.15C17.97,1.15 17.46,1.34
17.07,1.73L11.26,7.55L16.91,13.2L22.73,7.39C23.5,6.61 23.5,5.35
22.73,4.56L19.89,1.73C19.5,1.34 19,1.15
18.5,1.15M10.3,8.5L4.34,14.46C3.56,15.24 3.56,16.5
4.36,17.31C3.14,18.54
10,20.12L15.95,14.16"
/>
</svg>
</span>
)}
<div ref={ref}>
injected humour, or
501
years old. Richard McClintock, a Latin professor at Hampden-Sydney
College in Virginia,
source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de
Finibus Bonorum et
Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section
1.10.32.
</div>
</div>
);
};
Output
502
Batch api calls in sequence
Problem Statement -
You are given an info-graphic component where you have to batch call APIs
in sequence. Let’s say you have 20 APIs to call, batch call 5 APIs together,
and the next 5 after the previous one is done, and so on. The first call will
take after a delay of 5 seconds and once all the APIs are executed, reset and
start from the beginning.
Example
Input:
// API calls
6
,
10
11
12
13
14
15
16
,
17
18
19
20
We will use a dummy promise that will resolve after 1 second to mimic the
API call.
useEffect hook, check if the current index is first then make the call after the
5 seconds, and once all APIs are executed update the state and increment the
index. If the index is greater than the subarray size then reset it.
● After the index will update, the useEffect hook will be invoked and the
subsequent calls will be made, thus all the APIs will be executed
recursively.
● To make the API calls we will use a helper function that will execute all
the promises in parallel and increment the index after the operation.
503
import
{ useState, useEffect }
from
"react"
const
asyncTask =
function
){
return
new
Promise
((
resolve, reject
) => {
setTimeout(
()
=> resolve(
`Completing ${i}`
),
1000
);
});
};
const
chop = (
) => {
//temp array
const
temp = [...arr];
//output
const
output = [];
let
i=
while
(i < temp.length) {
output.push(temp.slice(i, i + size));
i = i + size;
return
output;
};
const
Example =
()
=> {
// array of promises
// 20
const
promises = [
asyncTask(
),
asyncTask(
),
asyncTask(
),
asyncTask(
),
asyncTask(
5
),
asyncTask(
),
asyncTask(
),
asyncTask(
),
504
asyncTask(
),
asyncTask(
10
),
asyncTask(
11
),
asyncTask(
12
),
asyncTask(
13
),
asyncTask(
14
),
asyncTask(
15
),
asyncTask(
16
),
asyncTask(
17
),
asyncTask(
18
),
asyncTask(
19
),
asyncTask(
20
),
];
const
subArrays = chop(promises,
);
const
);
// helper function to perform the async operations
const
asyncOperations =
async
(promises) => {
try
const
resp =
await
Promise
.all(promises);
console
.log(index, resp);
catch
(e) {
console
.log(e);
finally
? index
);
};
useEffect(
()
=> {
if
(index ===
){
setTimeout(
()
=> {
asyncOperations(subArrays[index]);
505
},
5000
);
one is done
else
asyncOperations(subArrays[index]);
}, [index]);
return
<></>
};
Output
// after 5 seconds
)[
'Completing 1'
'Completing 2'
'Completing
3'
'Completing 4'
,
'Completing 5'
)[
'Completing 6'
'Completing 7'
'Completing
8'
'Completing 9'
'Completing 10'
5
)[
'Completing 11'
'Completing 12'
'Completing
13'
'Completing 14'
'Completing 15'
)[
'Completing 16'
'Completing 17'
,
'Completing
18'
'Completing 19'
'Completing 20'
// after 5 seconds
)[
'Completing 1'
'Completing 2'
'Completing
3'
'Completing 4'
,
'Completing 5'
)[
'Completing 6'
'Completing 7'
'Completing
8'
'Completing 9'
'Completing 10'
(
5
)[
'Completing 11'
'Completing 12'
'Completing
13'
'Completing 14'
'Completing 15'
)[
'Completing 16'
'Completing 17'
,
'Completing
18'
'Completing 19'
'Completing 20'
506
Problem Statement -
string representing the time in the human-readable format like “just now”, “a
few secs ago”, “a minute ago”, “10 mins ago”, etc.
Example
Input:
<FormattedTime time=
{new
/>
Output:
3 hours ago
Get a time as input and get the difference between it with the current time in
seconds.
const
current = +
Date
.now();
const
lastTime = +lastDate;
let
diff =
Math
.abs(current - lastTime);
diff = diff /
1000
// messages
507
const
messages = {
NOW:
"just now"
LESS_THAN_A_MINUTE:
LESS_THAN_5_MINUTES:
MINUTES:
"mins ago"
,
HOURS:
"hours ago"
DAYS:
"days ago"
MONTHS:
"months ago"
YEARS:
"years ago"
};
// time in seconds
const
timeInSecond = {
MINUTE:
60
HOUR:
60
60
DAY:
24
60
60
MONTH:
30
24
60
60
,
YEAR:
365
24
60
60
};
Check, in which range do times fit and return the message accordingly.
switch
(diff) {
case
diff <
10
return
messages.NOW;
case
diff >
10
return
messages.LESS_THAN_A_MINUTE;
case
return
messages.LESS_THAN_5_MINUTES;
default
if
return
`${getFormatted(diff
/ timeInSecond.MINUTE)}
${messages.MINUTES}`;
return `${getFormatted(diff /
timeInSecond.HOUR)}
${messages.HOURS}`
else
if
timeInSecond.MONTH) {
return
`${getFormatted(diff
/ timeInSecond.DAY)}
${messages.DAYS}`;
return `${getFormatted(diff /
timeInSecond.MONTH)}
508
${messages.MONTHS}`
else
if
return
`${getFormatted(diff
/ timeInSecond.YEAR)}
${messages.YEARS}`;
Encapsulate this logic inside a function that will format and return the value.
// messages
const
messages = {
NOW:
"just now"
LESS_THAN_A_MINUTE:
"a few secs ago"
LESS_THAN_5_MINUTES:
MINUTES:
"mins ago"
HOURS:
"hours ago"
DAYS:
"days ago"
MONTHS:
"months ago"
YEARS:
"years ago"
,
};
// time in seconds
const
timeInSecond = {
MINUTE:
60
HOUR:
60
60
DAY:
24
60
60
MONTH:
30
24
60
60
YEAR:
365
24
60
60
};
const
getFormatted = (
time
) => {
return
Math
.floor(time);
};
const
calculate = (
lastDate
) => {
const
current = +
Date
.now();
509
lastTime = +lastDate;
let
diff =
Math
.abs(current - lastTime);
diff = diff /
1000
switch
(diff) {
case
diff <
10
return
messages.NOW;
case
diff >
10
return
messages.LESS_THAN_A_MINUTE;
case
return
messages.LESS_THAN_5_MINUTES;
default
if
return
`${getFormatted(diff
/ timeInSecond.MINUTE)}
${messages.MINUTES}`;
return `${getFormatted(diff /
timeInSecond.HOUR)}
${messages.HOURS}`
else
if
timeInSecond.MONTH) {
return
`${getFormatted(diff
/ timeInSecond.DAY)}
${messages.DAYS}`;
return `${getFormatted(diff /
timeInSecond.MONTH)}
${messages.MONTHS}`
;
}
else
if
return
`${getFormatted(diff
/ timeInSecond.YEAR)}
${messages.YEARS}`;
};
return <p>{convertedTime}</
p>;
};
510
Input:
<FormattedTime time=
{new
/>
Output:
3 hours ago
511
Problem Statement -
Draw circles on the screen on the click and whenever two circles
● If two or more circles overlap, change the background of the later circle.
Get the clientX and clientY coordinates when the user clicks and align the
circle around with a simple calculation so that it is placed in the center. Also
before updating the state check if the current circle is overlapping with the
existing circles then update the background color of the current.
const
draw = (
) => {
const
{ clientX, clientY } = e;
and placed
512
// as the circle is of 100 radius (200 diameter), we are subtracting the values
prevState
) => {
const
current = {
top: clientY -
100
left: clientX -
100
right: clientX -
100
200
bottom: clientY -
100
200
,
background:
"red"
};
for
let
i=
existing
if
(elementsOverlap(current, prevState[i]))
current.background = getRandomColor();
break
return
[...prevState, current];
});
};
Assign the event listener and draw the circle on the click.
useEffect(
()
=> {
document
.addEventListener(
"click"
, draw);
return
()
=> {
document
.removeEventListener(
"click"
, draw);
};
}, []);
513
const
getRandomColor =
()
=> {
const
letters =
"0123456789ABCDEF"
let
color =
"#"
for
let
i=
;i<
; i++) {
color += letters[
Math
.floor(
Math
.random() *
16
)];
return
color;
};
const
elementsOverlap = (
rect1, rect2
) => {
const
collide = !(
);
return
collide;
};
Generate the circles from the coordinates which we have stored after the user
has clicked. As the detection is done before making entry into the state, the
circles are generated with different colors if they collide.
// circle element
const
Circle = (
) => {
return
<div
style=
{{
position: "absolute",
width: "200px",
height: "200px",
borderRadius: "50%",
opacity: "0.5",
background,
top,
514
left,
}}
></div>
);
};
return
<div>
{elementsCoordinates.map((e) => (
{e.top
+ e.left + e.right}
/>
))}
</div>
);
import
{ useEffect, useState }
from
"react"
;
// helper function to generate a random color
const
getRandomColor =
()
=> {
const
letters =
"0123456789ABCDEF"
let
color =
"#"
for
let
i=
;i<
6
; i++) {
color += letters[
Math
.floor(
Math
.random() *
16
)];
return
color;
};
const
elementsOverlap = (
rect1, rect2
) => {
const
collide = !(
);
return
collide;
};
515
const
Example =
()
=> {
const
[elementsCoordinates, setElementsCoordinates]
= useState([]);
user clicks
const
draw = (
) => {
const
{ clientX, clientY } = e;
and placed
we are subtracting
the values
setElementsCoordinates((
prevState
) => {
const
current = {
top: clientY -
100
,
left: clientX -
100
right: clientX -
100
200
bottom: clientY -
100
200
background:
"red"
};
let
i=
any existing
if
(elementsOverlap(current, prevState[i]))
current.background = getRandomColor();
break
return
[...prevState, current];
});
};
useEffect(
()
=> {
document
.addEventListener(
"click"
, draw);
return
()
=> {
516
document
.removeEventListener(
"click"
, draw);
};
}, []);
// circle element
const
Circle = (
) => {
return
<div
style=
{{
position: "absolute",
width: "200px",
height: "200px",
borderRadius: "50%",
opacity: "0.5",
background,
top,
left,
}}
></div>
);
};
return
<div>
{elementsCoordinates.map((e) => (
{e.top
+ e.left + e.right}
/>
))}
</div>
);
};
517
Output
518
System Design
519
Overview
Make a point -> Validate the point -> And convince that the approach you
are explaining would be suited to the problem that you are dealing with.
make sure you get all the features set listed and then have your
will do more harm than good. If you are not certain about anything,
clearly convey that to the interviewers, they may provide hints.
In the next part we will see two system design questions. This book
was not meant to cover system design and I am little in-experienced in it.
Maybe in the future I will write a book around it too.
These are the two questions I have worked on in my professional life and the
same I am discussing next.
520
Problem Statement -
Imagine a corporate hierarchy where users with higher authority will have
privileges over the role with lower authority.
Based on their privileges and role, the features of the applications will be
available to the users.
In this order, the intern will have the least privilege and the senior manager
will have the most privilege.
A higher authority can update the role of the lower authority to any level
lower than it, for example, a Senior manager can promote an
If the lower authority makes any change that requires approval, the
task should be in a pending state and anyone above its role should be able to
approve it.
Based on this, design a web application for role-based users.
● How do you dynamically show the web application features based on the
user’s role?.
521
● How do you extend the application so that the role names can be dynamic?
● Things that you will handle in the frontend and things that you expect
from the backend.
● Route level restriction – where users with a certain role can access certain
routes.
● Feature level restriction – where users with certain roles and privileges
can perform the actions.
For example, an intern can view the employee hierarchy in the web
Thus after discussing with the product managers and getting clarity on the
roles and their privileges, we can design a complete hierarchy
Based on the configurations add the restrictions at the route level and the
features level. Call this configuration API before login in and cache it.
Once the user is logged in, based on their role, use the configuration details
and dynamically render the view.
user has the authority to access the route and then allow it .
522
Feature-level restrictions will answer the second point “How do you handle
the privileges and authority.”?
sub-page level.
A software engineer can only view the intern’s requests, a manager can view
both the software engineer’s and the intern’s requests, and so on.
How do you extend the application so that the role names can
be dynamic?
We can have alias mapping and can use the alias labels for the same
level. This way roles and privileges will remain the same but the labels could
be different.
523
Things that you will handle on the front end and things that you expect from
the back end.
The session management of the user will rely on the backend and the
and will be returned from the backend using which the frontend will
There should be checks to validate the privileges both at the backend and the
frontend to be extra sure.
The configuration JSON should be the single source of truth for both the
parts to minimize the chances of error.
524
Problem Statement -
application.
Let us tackle each point individually and then at the end we will
Problem listing
The problem listing page will have all the metadata related to the
problem like how many users have tried to solve the problem, what
They can also search the problem using a search bar with the fuzzy
search in the problem title and its description. All other options will be
available for filter thus it would not be necessary to search on that.
525
There would also be an option to show which problem you have successfully
solved so that you can filter them out.
Because a user won’t solve all the problems at once, we can add
implement the problem listing page with search and filter options. The filters
can be dynamic or static, aka DATA-TABLE “.
open-source online judge or paid one depending upon the requirements for
the core part of executing the coding and providing the result.
This page will be a two section layout, where in one part questions
along with sample test cases will be described and in the other section, the
IDE will be integrated.
The question section can also have the solutions and the past results tabs in
it.
526
This could be a multi-tab public forum, where registered users can ask and
answer queries posted by them. There will be an option to tag the questions
while asking and then filter based on these tags or using the search.
For answering, we can have one level reply for simplicity. Where only a
reply to the question will be given rather than another user’s reply.
Architecture
We can then load each of them in the main application and ship it.
Each bundle will be lazy-loaded.
This way the code will be split and being small bundles they will be faster to
load. The state between each can be shared using the local storage.
527
528
OceanofPDF.com