JavaScript Interview Guide - Preview (2)
JavaScript Interview Guide - Preview (2)
Concepts
1. Closure
2. Array.reduce() method
w
3. Promises
ie
4. Function & this
ev
JavaScript Questions
Pr
1. Promise.all() polyfill
2. Promise.any() polyfill
r
de
3. Promise.race() polyfill
4. Promise.finally() polyfill
5. Promise.allSettled() polyfill ui
G
6. Custom Promise
w
w
30. Create a sampling function
ie
31. Make function sleep
ev
32. Remove cycle from the object
33. Filter multidimensional array
Pr
34. Count element in multidimensional array
35. Convert HEX to RGB
r
de
36. Convert RGB to HEX
37. In-memory filesystem library
38. Basic implementations of streams API
ui
G
39. Create a memoizer function
w
w
63. Filter array of objects on value or index
ie
64. Aggregate array of objects on the given key
ev
65. Convert entity relation array to ancestry tree string
66. Get object value from string path
Pr
67. Set object value on string path
68. Polyfill for JSON.stringify()
r
de
69. Polyfill for JSON.parse()
70. HTML encoding of the string
71. CSS selector generator
ui
G
72. Aggregate the input values
w
w
96. Concurrent history tracking system
ie
97. Implement an in-memory search engine
ev
98. Implement a fuzzy search function
99. Cancel an API request
Pr
100. Highlight words in the string
r
de
React Questions
1. usePrevious() hook
2. useIdle() hook ui
G
3. useAsync() hook
w
4. useDebounce() hook
ie
5. useThrottle() hook
rv
6. useResponsive() hook
te
7. useWhyDidYouUpdate() hook
8. useOnScreen() hook
In
9. useScript() hook
t
rip
w
ie
ev
Pr
r
de
ui
G
w
ie
rv
te
t In
rip
Sc
va
Ja
w
Example
ie
Input:
ev
retry(asyncFn, retries = 3, delay = 50, finalError = 'Failed');
Pr
Output:
... attempt 1 -> failed
r
... attempt 2 -> retry after 50ms -> failed
de
... attempt 3 -> retry after 50ms -> failed
... Failed.
ui
G
w
In short, we have to create a retry function that Keeps on retrying until
ie
We will see two code examples one with thenable promise and the
te
Let us first create the delay function so that we can take a pause for a
rip
Delay function
va
//delay func
const wait = ms => new Promise((resolve) => {
setTimeout(() => resolve(), ms);
});
w
function or else reject with the final error.
ie
Implementation
ev
const retryWithDelay = (
Pr
operation, retries = 3,
delay = 50, finalErr = 'Retry failed') => new Promise((resolve, reject)
r
=> {
de
return operation()
.then(resolve)
ui
.catch((reason) => { G
//if retries are left
if (retries > 0) {
w
//delay the next call
ie
return wait(delay)
//recursively call the same function to retry with max retries -
rv
1
te
.then(resolve)
.catch(reject);
t
}
rip
return reject(finalErr);
});
va
});
Ja
Test Case
To test we can create a test function that keeps throwing errors if
called less than 5 times. So if we call it 6 or more times, it will pass.
Input:
w
throw new Error('Not yet');
ie
}
}
ev
}
Pr
// Test the code
const test = async () => {
r
de
await retryWithDelay(getTestFunc(), 10);
console.log('success');
await retryWithDelay(getTestFunc(), 3);
console.log('will fail before getting here');
ui
G
}
w
test().catch(console.error);
rv
Output:
te
still left then recursively call the same function or else throw the final
Ja
error.
Implementation
const retryWithDelay = async (
fn, retries = 3, interval = 50,
finalErr = 'Retry failed'
w
return Promise.reject(finalErr);
ie
}
ev
//delay the next call
await wait(interval);
Pr
//recursively call the same func
r
de
return retryWithDelay(fn, (retries - 1), interval, finalErr);
}
}
ui
G
Test Case
w
ie
Input:
// Test function
rv
let callCounter = 0;
return async () => {
In
callCounter += 1;
// if called less than 5 times
t
rip
// throw error
if (callCounter < 5) {
Sc
}
}
Ja
Output:
"success" // 1st test
"Retry failed" //2nd test
w
Alternatively, you can also update the code and keep on retrying the
ie
API call based on some test.
ev
Pr
r
de
ui
G
w
ie
rv
te
t In
rip
Sc
va
Ja
w
error occurs. It also accepts a limit to decide how many operations can
ie
occur at a time.
ev
Pr
The asynchronous iteratee function will accept an input and a
callback. The callback function will be called when the input is
r
de
finished processing, the first argument of the callback will be the error
flag and the second will be the result.
ui
Example
G
w
Input:
let numPromise = mapLimit([1, 2, 3, 4, 5], 3, function (num, callback) {
ie
rv
setTimeout(function () {
In
num = num * 2;
console.log(num);
t
callback(null, num);
rip
}, 2000);
});
Sc
numPromise
va
Output:
/// first batch
2
4
6
/// second batch
w
ie
● First chop the input array into the subarrays of the given limit.
ev
This will return us an array of arrays like [[1, 2, 3], [4, 5]].
Pr
● The parent array will run in series, that is the next subarray will
execute only after the current subarray is done.
r
● All the elements of each sub-array will run in parallel.
de
● Accumulate all the results of each sub-array element and resolve
the promise with this.
ui
G
● If there is any error, reject.
w
ie
//temp array
te
return temp;
}
Sc
//output
va
w
// [[1, 2, 3], [1, 2, 3]]
ie
let chopped = arr.chop(limit);
ev
// for all the subarrays of chopped
// run it in series
Pr
// that is one after another
// initially it will take an empty array to resolve
r
de
// merge the output of the subarray and pass it on to the next
const final = chopped.reduce((a, b) => {
ui
// listen on the response of previous value
G
return a.then((val) => {
w
let tasksCompleted = 0;
b.forEach((e) => {
Sc
if(error){
reject(error);
}else{
results.push(value);
tasksCompleted++;
if (tasksCompleted >= b.length) {
});
w
}, Promise.resolve([]));
ie
// based on final promise state
ev
// invoke the final promise.
final
Pr
.then((result) => {
resolve(result);
r
de
})
.catch((e) => {
reject(e);
});
ui
G
});
};
w
ie
Input:
te
num = num * 2;
console.log(num);
t
rip
callback(null, num);
}, 2000);
Sc
});
va
numPromise
.then((result) => console.log("success:" + result))
Ja
Output:
// first batch
2
4
6
// second batch
w
setTimeout(function () {
ie
num = num * 2;
console.log(num);
ev
// throw error
Pr
if(num === 6){
callback(true);
r
de
}else{
callback(null, num);
}
ui
G
}, 2000);
});
w
ie
numPromise
.then((result) => console.log("success:" + result))
rv
Output:
In
// first batch
2
t
rip
4
6
Sc
"no success"
va
Ja
w
back.
ie
ev
It is one of the classic problems which use two of the trickiest concepts
of JavaScript.
Pr
1. Timers.
r
de
2. Closure.
ui
Use the setInterval to auto increment the values, whereas wrapping
G
the start and stop function inside the closure and returning them, so
w
that the incrementor can be controlled while still maintaining the
ie
value.
rv
Our incrementor function will take two inputs, initial value, and steps.
And within the function, we will need two variables,
t
rip
w
const startTimer = () => {
ie
if (!intervalId){
ev
intervalId = setInterval(() => {
console.log(count);
Pr
count += step;
}, 1000);
r
}
de
}
ui
There is a condition to check if intervalId is having any value or not,
G
just to make sure we don’t start multiple intervals.
w
Stop function
ie
intervalId to null.
In
clearInterval(intervalId);
rip
intervalId = null;
}
Sc
return {
Ja
startTimer,
stopTimer,
};
w
intervalId = setInterval(() => {
console.log(count);
ie
count += step;
ev
}, 1000);
}
Pr
}
r
de
clearInterval(intervalId);
intervalId = null;
ui
} G
return {
startTimer,
w
stopTimer,
ie
};
rv
}
te
Test Case
In
Input:
t
//start
timerObj.startTimer();
Sc
//stop
va
setTimeout(() => {
timerObj.stopTimer();
Ja
}, 5000);
Output:
10
20
30
40
w
ie
Example
ev
Input:
2 5 17 23 88 54 1 22
Pr
Output:
r
max: 88
de
min: 1
Approach ui
G
● The idea is very simple, instead of storing a single value in the
w
stack we will store an object with current, max and min values.
ie
● While adding the new value in the stack we will check if the stack
rv
is empty or not.
te
● If it is empty then add the current value as current, max and min
In
values.
● Else get the previous value and compare it with the current item,
t
rip
Implementation
va
function stackWithMinMax(){
Ja
w
ie
//If it is lesser than previous then update the min
min = min > item ? item : min;
ev
//Add the new data
Pr
items[length++] = {value: item, max, min};
}
r
de
}
}
In
this.min = () => {
return items[length - 1].min;
Ja
w
}
ie
}
ev
Test Case
Pr
Input:
let SM = new stackWithMinMax();
r
de
SM.push(4);
SM.push(7);
SM.push(11);
SM.push(23);
ui
G
SM.push(77);
SM.push(3);
w
SM.push(1);
ie
SM.pop();
console.log(`max: ${SM.max()}`, `min: ${SM.min()}`);
rv
te
Output:
"max: 77" "min: 3"
t In
rip
Sc
va
Ja
w
Example
ie
Input:
ev
const List = function(val){
this.next = null;
Pr
this.val = val;
};
r
de
const item1 = new List(10);
const item2 = new List(20);
const item3 = new List(30);
ui
G
item1.next = item2;
w
item2.next = item3;
ie
item3.next = item1;
rv
// this form a cycle, if you console.log this you will see a circular
te
object,
// like, item1 -> item2 -> item3 -> item1 -> so on.
In
Output:
t
rip
// removes cycle
// item1 -> item2 -> item3
Sc
If you see the above example, we have created a list object, that accepts
va
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.
Ja
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.
Normal use
w
We can use WeakSet which is used to store only unique object
ie
references and detect if the given object was previously detected or
ev
not, if it was detected then delete it.
Pr
This cycle removal always takes in-place.
r
const removeCycle = (obj) => {
de
//set store
const set = new WeakSet([obj]);
ui
G
//recursively detects and deletes the object references
(function iterateObj(obj) {
w
for (let key in obj) {
// if the key is not present in prototype chain
ie
if (obj.hasOwnProperty(key)) {
rv
// then delete it
In
if (set.has(obj[key])){
delete obj[key];
t
}
rip
else {
//store the object reference
Sc
set.add(obj[key]);
//recursively iterate the next objects
va
iterateObj(obj[key]);
}
Ja
}
}
}
})(obj);
}
w
const item1 = new List(10);
const item2 = new List(20);
ie
const item3 = new List(30);
ev
item1.next = item2;
Pr
item2.next = item3;
item3.next = item1;
r
de
removeCycle(item1);
console.log(item1);
Output:
ui
G
/*
{val: 10, next: {val: 20, next: {val: 30}}}
w
*/
ie
rv
We can use the same function to detect and remove the cycle from the
Sc
object.
va
w
ie
Test Case
ev
Input:
const List = function(val){
Pr
this.next = null;
this.val = val;
r
de
};
item2.next = item3;
item3.next = item1;
rv
te
console.log(JSON.stringify(item1, getCircularReplacer()));
In
Output:
"{'next':{'next':{'val':30},'val':20},'val':10}"
t
rip
Sc
va
Ja
w
Example
ie
Input:
ev
computeAmount().lacs(15).crore(5).crore(2).lacs(20).thousand(45).crore(7).v
alue();
Pr
Output:
r
143545000
de
ui
In the previous problem we have already seen an example of method
chaining in which we used object-based and function-based
G
implementations.
w
ie
this.store = 0;
va
this.crore = function(val){
this.store += val * Math.pow(10, 7);
Ja
return this;
};
this.lacs = function(val){
this.store += val * Math.pow(10, 5);
return this;
}
this.hundred = function(val){
this.store += val * Math.pow(10, 2);
w
return this;
ie
}
ev
this.ten = function(val){
this.store += val * 10;
Pr
return this;
}
r
de
this.unit = function(val){
this.store += val;
return this;
ui
G
}
w
this.value = function(){
ie
return this.store;
}
rv
}
te
Test Case
In
Input:
t
rip
computeAmount.lacs(15).crore(5).crore(2).lacs(20).thousand(45).crore(7).val
ue();
va
Output:
true
w
ie
const ComputeAmount = function(){
ev
return {
Pr
store: 0,
crore: function(val){
r
this.store += val * Math.pow(10, 7);
de
return this;
},
lacs: function(val){
ui
G
this.store += val * Math.pow(10, 5);
w
return this;
},
ie
rv
thousand: function(val){
this.store += val * Math.pow(10, 3);
te
return this;
In
},
t
hundred: function(val){
rip
},
va
ten: function(val){
this.store += val * 10;
Ja
return this;
},
unit: function(val){
this.store += val;
return this;
},
Test Case
w
ie
Input:
const amount =
ev
ComputeAmount().lacs(9).lacs(1).thousand(10).ten(1).unit(1).value();
console.log(amount === 1010011);
Pr
const amount2 =
r
de
ComputeAmount().lacs(15).crore(5).crore(2).lacs(20).thousand(45).crore(7).v
alue();
console.log(amount2 === 143545000);
ui
G
Output:
true
w
true
ie
rv
te
t In
rip
Sc
va
Ja
w
add(1)(2)(3).value() = 6;
ie
add(1)(2) + 3 = 6;
ev
This is a little tricky question and requires us to use and modify the
Pr
valueOf() method.
r
de
When JavaScript wants to turn an object into a primitive value, it uses
the function valueOf() method. JavaScript automatically calls the
ui
function valueOf() method when it comes across an object where a
G
primitive value is anticipated, so you don’t ever need to do it yourself.
w
Example
ie
function MyNumberType(n) {
rv
this.number = n;
te
}
In
MyNumberType.prototype.valueOf = function () {
return this.number + 1;
t
rip
};
Sc
Thus we can form a closure and track the arguments in an Array and
Ja
We will also override the valueOf() method and return the sum of all
the arguments for each primitive action, also add a new method
function add(...x){
// store the current arguments
let sum = x;
w
function resultFn(...y){
ie
// merge the new arguments
sum = [...sum, ...y];
ev
return resultFn;
}
Pr
// override the valueOf to return sum
r
de
resultFn.valueOf = function(){
return sum.reduce((a, b) => a + b, 0);
};
ui
G
// extend the valueOf
resultFn.value = resultFn.valueOf;
w
ie
return resultFn;
}
In
Test Case
t
rip
Input:
Sc
console.log(add(1)(2).value() == 3);
console.log(add(1, 2)(3).value() == 6);
va
console.log(add(1)(2)(3).value() == 6);
console.log(add(1)(2) + 3);
Ja
Output:
true
true
true
6
w
ie
Example
ev
Input:
Pr
const str = 'Hello, world';
const styleArr = [[0, 2, 'i'], [4, 9, 'b'], [7, 10, 'u']];
r
de
Output:
'<i>Hel</i>l<b>o, w<u>orl</u></b><u>d</u>'
ui
Note – <u> tag gets placed before the <b> tag and after it as the
G
insertion index overlaps it.
w
ie
Let us first see the second method as most of the times in the interview
you will be asked to implement this way only.
Ja
Output:
<i>H<b>el</b></i><b>l</b>o, World
w
// b is starting from 1 and ending at 3, i is in between b.
ie
ev
As you can see in the above example b is overlapping thus it is closed
at 2nd character of the string and re-opened at 3.
r Pr
We can solve this with the help of a priority queue and a stack.
de
ui
● Aggregate the styles on the starting index in the order of priority
G
of the range difference between them. (endIndex – startIndex),
this way the longest tag is created first.
w
one, then create a new tag as it is overlapping and push this new
te
entry back to the stack and in the next iteration create this tag.
In
● At the end return the string of the top most stack entry.
t
rip
Priority queue
We are not going to use the complete implementation of Priority
Sc
Queue, rather a helper function that adds a new item in the array and
va
Stack
A simple implementation of the Stack data structure with the required
methods.
w
function Stack() {
ie
let items = [];
ev
let top = 0;
Pr
//Push an item in the Stack
this.push = function (element) {
items[top++] = element;
r
de
};
ui
//Pop an item from the Stack
this.pop = function () {
G
return items[--top];
};
w
ie
this.peek = function () {
return items[top - 1];
te
};
In
};
t
Tag method
rip
A helper function to create a new tag for each entry of styles and its
Sc
corresponding, also helps to get the range for easier processing in the
priority queue. We push this in the priority queue and sort it.
va
w
ie
● Create an empty array trace of the size of the input string.
ev
● Iterate the styles and form a new tag for each entry.
● On the start index of the styles aggregate the common tags in the
Pr
priority queue based on the difference of their range in
r
descending order.
de
● Add these priority queues at the start indexes of the styles in the
ui
trace. G
● Create a new stack and add an initial Tag with maximum range
i.e Tag(start = 0, end = Number.MAX_VALUE, tag = "").
w
● Iterate till input string length, get the tags from the trace at each
ie
● Create the opening HTML tag for each tag in the queue and push
te
it in the stack. Check if the current end Index of the tag is greater
In
than the previous one in the stack, then create a new tag and
push it in the priority queue.
t
rip
● At the end close all the HTML tags at the given index in the loop.
Sc
// initialize with a new Tag that has max range and empty string
html.push(new Tag(0, Number.MAX_VALUE, ""));
w
ie
// check for opening tags and add them
while (track[i] && track[i].length > 0) {
ev
const cur = track[i].shift();
cur.text = `<${cur.tag}>`;
Pr
// for example in [0, 2, 'i'] , [1, 3, 'b']
r
de
// b is starting from 1 and ending at 3, i is in between b.
// <i> <b> </b> </i> <b> </b>
// if the end of the nested tag is larger than the parent,
// split the tag
ui
G
// and insert the remaining split to the bucket after its parent
if (cur.end > html.peek().end) {
w
cur.end = html.peek().end;
addAndSort(track, html.peek().end + 1, split);
rv
}
te
html.push(cur);
}
t
rip
html.peek().text += str[i];
va
html.peek().text += `</${html.peek().tag}>`;
const temp = html.pop().text;
html.peek().text += temp;
}
}
Test Case
Input:
const encoded = parse('Hello, World',
[[0, 2, "i"],
[7, 10, "u"],
w
[4, 9, "b"],
ie
[2, 7, "i"],
[7, 9, "u"]]);
ev
console.log(encoded);
Pr
Output:
r
de
"<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, World"
ui
G
w
ie
Using DOMParser()
rv
DOMParser() parses the HTML source code from the string and the
te
is missing.
t
rip
Thus all we have to do is traverse the styles and add tags at the
mentioned opening and closing positions in the input string.
Sc
Then pass this string to the DOMParser() and it will generate the
va
w
ie
Test Case
ev
Input:
const encoded = parse('Hello, World',
Pr
[[0, 2, "i"],
[7, 10, "u"],
r
de
[4, 9, "b"],
[2, 7, "i"],
[7, 9, "u"]]);
ui
G
console.log(encoded);
w
Output:
ie
"<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"
rv
"Hello, World"
te
t In
rip
Sc
va
Ja
w
Example
ie
document.myCookie = "blog=learnersbucket";
ev
document.myCookie = "name=prashant;max-age=1"; // this will expire after 1
second
Pr
console.log(document.myCookie);
r
// "blog=learnersbucket; name=prashant"
de
setTimeout(() => {
console.log(document.myCookie);
ui
}, 1500);
G
// "blog=learnersbucket"
w
// "name=prashant" is expired after 1 second
ie
like get() and set() that could be used behind the scene to make the
t
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
them in the map.
● 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
To parse the input and separate the data and options, we will be using
a helper function.
w
// key-value pairs
ie
function parseCookieString(str) {
// split the string on ;
ev
// separate the data and the options
const [nameValue, ...rest] = str.split(';');
Pr
// get the key value separated from the data
r
const [key, value] = separateKeyValue(nameValue);
de
// parse all the options and store it
// like max-age
ui
const options = {};
G
for(const option of rest) {
w
const [key, value] = separateKeyValue(option);
ie
options[key] = value;
}
rv
te
return {
key,
In
value,
options,
t
rip
}
}
Sc
// helper function
// to separate key and value
va
function separateKeyValue(str) {
Ja
For the main logic we can extend the document object with
Object.defineProperty().
Object.defineProperty(document, 'myCookie', {
w
configurable: true,
ie
get() {
ev
const cookies = [];
const time = Date.now();
Pr
// get all the entries from the store
r
de
for(const [name, { value, expires }] of store) {
// if the entry is expired
// remove it from the store
if (expires <= time) {
ui
G
store.delete(name);
}
w
else {
cookies.push(`${name}=${value}`);
rv
}
te
}
In
},
Sc
set(val) {
// get the key value of the data
va
Test Case
w
Input:
ie
useCustomCookie();
document.myCookie = "blog=learnersbucket";
ev
// this will expire after 1 second
Pr
document.myCookie = "name=prashant;max-age=1";
r
de
console.log(document.myCookie);
setTimeout(() => {
console.log(document.myCookie);
ui
G
}, 1500);
w
Output:
ie
"blog=learnersbucket; name=prashant"
"blog=learnersbucket"
rv
te
t In
rip
Sc
va
Ja
w
● When a user clicks anywhere on the DOM, create a circle around it of
ie
a radius of 100px with a red background.
ev
● If two or more circles overlap, change the background of the later
Pr
circle.
r
Let us understand the logic of creating the circle first.
de
ui
As we have to create the circle with a radius of 100px (200px diameter),
rather than generating the circle on the click, we will store the coordinates
G
of the position where the circle should be generated when the user clicks
w
As all the circles will be in absolute position so that they can be freely
placed on the screen, we will calculate the top, bottom, left, and right
te
positions that will help in placement as well as detecting if two circles are
In
colliding.
t
rip
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
Sc
before updating the state check if the current circle is overlapping with the
va
w
right: clientX - 100 + 200,
ie
bottom: clientY - 100 + 200,
background: "red",
ev
};
Pr
// before making the new entry
// check with the existing circles
r
de
for (let i = 0; i < prevState.length; i++) {
// if the current circle is colliding with any existing
// update the background color of the current
ui
if (elementsOverlap(current, prevState[i])) {
G
current.background = getRandomColor();
break;
w
}
ie
}
rv
});
};
In
Assign the event listener and draw the circle on the click.
t
rip
Sc
document.addEventListener("click", draw);
return () => {
Ja
document.removeEventListener("click", draw);
};
}, []);
w
color += letters[Math.floor(Math.random() * 16)];
ie
}
return color;
ev
};
Pr
// helper function to detect if two elements are overlapping
const elementsOverlap = (rect1, rect2) => {
r
const collide = !(
de
rect1.top > rect2.bottom ||
rect1.right < rect2.left ||
ui
rect1.bottom < rect2.top || G
rect1.left > rect2.right
);
w
return collide;
ie
};
rv
te
Generate the circles from the coordinates which we have stored after
the user has clicked. As the detection is done before making entry into
In
the state, the circles are generated with different colors if they collide.
t
rip
// circle element
Sc
<div
style={{
Ja
position: "absolute",
width: "200px",
height: "200px",
borderRadius: "50%",
opacity: "0.5",
background,
top,
return (
<div>
w
{/* render each circle */}
ie
{elementsCoordinates.map((e) => (
<Circle {...e} key={e.top + e.left + e.right} />
ev
))}
</div>
Pr
);
r
de
Putting everything together.
ui
import { useEffect, useState } from "react";
G
w
// helper function to generate a random color
const getRandomColor = () => {
ie
}
return color;
t
};
rip
return collide;
};
w
ie
// decide the position where circle will be created and placed
// as the circle is of 100 radius (200 diameter), we are subtracting
ev
the values
// so that circle is placed in the center
Pr
// set the initial background color to red
setElementsCoordinates((prevState) => {
r
de
const current = {
top: clientY - 100,
left: clientX - 100,
right: clientX - 100 + 200,
ui
G
bottom: clientY - 100 + 200,
background: "red",
w
};
ie
current.background = getRandomColor();
break;
Sc
}
}
va
});
};
// circle element
const Circle = ({ top, left, background }) => {
return (
<div
style={{
w
position: "absolute",
ie
width: "200px",
height: "200px",
ev
borderRadius: "50%",
opacity: "0.5",
Pr
background,
top,
r
de
left,
}}
></div>
);
ui
G
};
w
return (
ie
<div>
{/* render each circle */}
rv
{elementsCoordinates.map((e) => (
te
</div>
);
t
rip
};
Sc
Output
Ja