CARS24 Frontend Interview Questions with Expert Answers

CARS24 Frontend Interview Questions with Expert Answers


Hello everyone, today we will cover some interview questions asked in Frontend Developer rounds at CARS24.

CARS24 is an Indian multinational online marketplace for used cars headquartered in Gurgaon. The company is considered among the four major organised players in the used car segment in India. Its revenue was close to $660 million in 2023.

If that got you excited, then let's start with interview questions for the day:

Q1. Write polyfill for debounce and throttle

Let’s understand these concepts one by one:

Debounce:

The debounce function ensures that a function is executed only once in a given time interval, even if repeated calls are made to it. It is useful when we want to limit the rate at which a function executes in a given time interval, especially when dealing with user-triggered events like typing in a search box, resizing the window, or scrolling.

For example when a user types, a search API call can go to the server on every keystroke which is resource-intensive and might confuse the user due to frequent out-of-order changes in search results. Using debouncing, we can ensure that search API calls will execute only after the user stops typing for 500 milliseconds so that results are relevant and resources are not wasted unnecessarily.

Polyfill for debounce

// Polyfill for debounce
function debounce(func, functionDelay) {
  let timerForNextCall;
  return function() {
    clearTimeout(timerForNextCall);
    timerForNextCall = setTimeout(func, functionDelay);
  };
}

// Usage
const handleWindowResize = debounce(() => {
  console.log('Resizing window...');
}, 500);

window.addEventListener('resize', handleWindowResize);

Code Explanation

  • When debounce is called with a function, we set timeout to execute the function after some delay as shown in timerForNextCall

  • If the function is called again we cancel the previous timeout to stop the previous function execution and then set the timeout again to execute the function later.

  • If the event triggering the function (e.g., resize) stops firing for the functionDelay duration, the function (func) will execute after that delay.

Throttle:

The throttle function ensures that a function is invoked only once in a specified time interval even if there are multiple calls to the function. It is useful when you want to regulate the execution frequency of a function, especially in high-frequency events like scrolling or window resizing.

For example, in a scrolling event, you may want to update something every 200ms rather than firing updates continuously.

Polyfill for Throttle

function throttle(func, timeLimit) {
  let inThrottle;
  return function() {
    if (!inThrottle) {
      func();
      inThrottle = true;
      setTimeout(() => inThrottle = false, timeLimit);
    }
  };
}

// Usage
const handleUserScroll = throttle(() => {
  console.log('Scrolling...');
}, 500);

window.addEventListener('scroll', handleUserScroll);

Code Explanation

  • inThrottle is a flag that controls whether the function is allowed to be executed or not.

  • When the function is first called, func executes immediately, and the inThrottle flag is set to true.

  • The setTimeout will reset the flag after the limit (500ms in this case), allowing the function to execute again.

Bonus Question: Difference between Debounce and Throttle

  • Debounce ensures that a function is called only after a specified delay while throttle ensures that a function is called at most once in a specified time interval.

  • In debounce, if the event is triggered again within that delay period, the previous execution is cancelled, and the timer restarts. While in throttle once the function is executed, it will ignore any subsequent triggers until the specified interval has passed.

Personal tip: A few variations of this question are asked commonly by a lot of other companies in frontend interview rounds so make sure to understand the concepts well.


Phew, that was a lot of reading so if you want to take a break then now is the good time to bookmark this article on your reading list, drink some water and come back for the next question. P.S. It's also good to take a break to reduce eye strain by continuous screen usage.

If you are back then let's continue towards the second question for the interview.


Q2. Merge Two Sorted Linked Lists

Problem statement: You are given two sorted linked lists. You have to merge them to produce a combined sorted linked list. You need to return the head of the final linked list.

Condition:
The given linked lists may or may not be null.
Example:
If the first list is: 1 -> 4 -> 5 -> NULL

and the second list is: 2 -> 3 -> 5 -> NULL

The final list would be: 1 -> 2 -> 3 -> 4 -> 5 -> 5 -> NULL

Input: The first line of input contains an integer 'T' representing the number of test cases or queries to be processed. Then the test case follows. The first line of each test case contains the elements of the first linked list separated by a single space and terminated by -1. Hence, -1 would never be a list element. The second line of each test case contains the elements of the second linked list separated by a single space and terminated by -1

Output Format:
For each test case, print the final linked list. The elements of the linked list must be separated by a single space and terminated by -1. Print the output of each test case in a separate line.

In this question, linked lists are in sorted order so what we can do is traverse them simultaneously and start appending values to a third linked list by taking smaller of the two lists sequentially.

Approach:

  • We first traverse both lists, compare the current nodes, and append the smaller node to the result list.

  • Once one list is exhausted, we append the remaining nodes from the other list.

  • We repeat this process for all test cases.

Implementation Steps:

  • We will parse the input to extract the two lists for each test case.

  • Then we will convert the arrays into linked lists.

  • Next, we merge the two linked lists using a helper function.

  • Now we convert the merged linked list back into the output format.

  • Finally, we print the results for all test cases.

Pseudocode:

FUNCTION mergeTwoLinkedLists(ll1, ll2):
    Create a dummy node to simplify the merge process.
    Set current pointer to dummy.

    WHILE ll1 is not null AND ll2 is not null:
        IF ll1 value is less than or equal to ll2 value:
            Append ll1 node to result.
            Move ll1 to next node.
        ELSE:
            Append ll2 node to result.
            Move ll2 to next node.
        Move current to next node.

    Append the remaining nodes of ll1 or ll2 to the result.

    RETURN dummy.next (head of merged list).

FUNCTION processTestCases(input):
    Parse the number of test cases, T.
    FOR each test case:
        Convert the input arrays into linked lists.
        Call mergeTwoLinkedLists() with the two lists.
        Convert the merged list back to an array.
        Append the result to output.

    Print all results.

MAIN:
    Read input.
    Call processTestCases(input).

Implementation in Javascript:

// Node class to represent a linked list node containing node value and pointer to next node
class LinkedListNode {
    constructor(val) {
        this.val = val;  // Value of the node
        this.next = null;  // Pointer to the next node
    }
}

// Utility function to convert an array to a linked list
function convertArrayToLinkedList(inputArray) {
    let dummy = new LinkedListNode(0);  // Dummy node for easy construction
    let current = dummy;
    for (let val of inputArray) {
        current.next = new LinkedListNode(val);  // Append new node
        current = current.next;  // Move to next
    }
    return dummy.next;  // Return the head of the linked list
}

// Utility function to convert a linked list to an array
function convertLinkedListToArray(head) {
    let result = [];
    while (head) {
        result.push(head.val);  // Add node value to array
        head = head.next;  // Move to next node
    }
    return result;
}

// Function to merge two sorted linked lists
function mergeTwoLinkedLists(ll1, ll2) {
    let dummy = new LinkedListNode(0);  // Dummy node for result list
    let current = dummy;

    // Merge process
    while (ll1 !== null && ll2 !== null) {
        if (ll1.val <= ll2.val) {
            current.next = ll1;  // Append ll1 node
            ll1 = ll1.next;  // Move ll1 to next
        } else {
            current.next = ll2;  // Append ll2 node
            ll2 = ll2.next;  // Move ll2 to next
        }
        current = current.next;  // Move current to next
    }

    // Append the remaining nodes
    current.next = ll1 !== null ? ll1 : ll2;

    return dummy.next;  // Return the head of the merged list
}

// Function to process multiple test cases
function processTestCases(input) {
    const lines = input.trim().split("\n");
    const noOfTestCases = parseInt(lines[0]);  // Number of test cases
    let result = [];

    for (let i = 0; i < noOfTestCases; i++) {
        // Parse input lists for each test case
        const list1 = lines[2 * i + 1]
            .split(" ")
            .map(Number) // Convert strings to integer using map
            .slice(0, -1); // Exclude -1
        const list2 = lines[2 * i + 2]
            .split(" ")
            .map(Number) // Convert strings to integer using map
            .slice(0, -1); // Exclude -1

        // Convert arrays to linked lists
        const ll1 = convertArrayToLinkedList(list1);
        const ll2 = convertArrayToLinkedList(list2);

        // Merge the two linked lists
        const mergedList = mergeTwoLinkedLists(ll1, ll2);

        // Convert the merged list to output format
        result.push(convertLinkedListToArray(mergedList).join(" ") + " -1");
    }

    return result.join("\n");
}

// Example
const input = `2
1 4 5 -1
2 3 5 -1
-1 -1
-1 -1`;
console.log("input lists", input);
console.log("output lists", processTestCases(input));

Output:-
"input lists" "2
1 4 5 -1
2 3 5 -1
-1 -1
-1 -1"

"output lists" "1 2 3 4 5 5 -1
-1 -1 -1"

Explanation

Input:
2
1 4 5 -1
2 3 5 -1
-1 -1
-1 -1

Steps:

  1. Test Case 1:
  • First, we convert the input array 1 4 5 -1 to Linked list 1 -> 4 -> 5 -> NULL.

  • Next, we convert 2 3 5 -1 → Linked list: 2 -> 3 -> 5 -> NULL.

  • Next, we merge the two lists:
    Compare 1 and 2: Add 1.
    Compare 4 and 2: Add 2.
    Compare 4 and 3: Add 3.
    Compare 4 and 5: Add 4.
    Compare 5 and 5: Add 5, then add other remaining5.

  • Result: 1 -> 2 -> 3 -> 4 -> 5 -> 5 -> NULL.

  • Finally, we convert the linked list to an output array : 1 2 3 4 5 5 -1.

Test Case 2:

  • Both lists are empty (-1 -1).

  • So the result is -1.

Time Complexity:

The time complexity of the above approach is O(N) since we use single loops in converting an array to a linked list and vice versa. Also, the merge lists function uses a single while loop. Since all three first-level loops are without nested loops, time complexity remains at O(N).


Well, that’s it from my end for today. This article is part of the JSInterview30 series I am building, so follow me for the next article in the series. Show some appreciation to keep getting more such content in your feed thereby improving your chances of cracking an interview. See you all in the next article. Goodbye 👋🏼

Questions Reference: AmbitionBox

Article originally published on Medium.

Did you find this article valuable?

Support saurabhnative by becoming a sponsor. Any amount is appreciated!