Karat-Style Technical Interviews: A Complete JavaScript Practice Guide with Solutions

Karat-Style Technical Interviews: A Complete JavaScript Practice Guide with Solutions
Technical interviews can feel difficult even for experienced developers. The challenge is not only finding the correct solution. You must also understand the requirements, explain your thinking, select suitable data structures, write working code, test it, and discuss its performance—all while an interviewer observes your process.
Karat-style interviews are designed to evaluate this complete problem-solving process. According to the official Karat Candidate Experience, a standard interview generally lasts around one hour and contains three main sections:
A brief introduction
Approximately 10 minutes of technical discussion
Approximately 40 minutes of programming questions
The exact format can vary depending on the hiring company and position. Some companies may begin with a shorter 30-minute coding assessment before inviting candidates to a complete live interview.
This guide explains the most important Karat-style question patterns and provides original JavaScript practice problems with complete solutions. These examples are not leaked or official Karat questions. They are designed to help you practise the same skills and interview structure.
What Does a Karat-Style Interview Evaluate?
The interviewer is not evaluating only the final code. They are also observing how you work through the problem.
They usually look for the following skills:
Understanding and clarifying requirements
Breaking a problem into smaller steps
Choosing appropriate data structures
Explaining technical decisions
Writing clean and understandable code
Identifying edge cases
Testing and debugging
Analysing time and space complexity
Responding to changing requirements
A candidate may produce an incomplete solution but still demonstrate strong engineering skills. On the other hand, silently writing a memorized solution may not provide enough evidence of genuine problem-solving ability.
A Reliable Process for Answering Coding Questions
Before studying individual problems, you should learn a consistent answering process.
1. Confirm Your Understanding
Do not begin coding immediately. First, repeat the requirement in your own words.
You can say:
Let me confirm my understanding. The function receives a list of activity records, and I need to return the users who satisfy all the required conditions.
This gives the interviewer an opportunity to correct any misunderstanding before you spend time implementing the wrong solution.
2. Ask Clarifying Questions
Useful questions include:
Can the input be empty?
Can it contain duplicate values?
Is the input already sorted?
Can I modify the original input?
What should I return if no result exists?
Does the result order matter?
How large can the input be?
Are all values guaranteed to be valid?
Do not ask questions only to impress the interviewer. Ask questions that could genuinely change your implementation.
3. Explain Your Approach
Before coding, explain the main idea.
For example:
A simple solution would compare every item with every other item, but that would take O(n²) time. We can improve it to O(n) by storing previously seen values in a Map.
This demonstrates that you understand both the simple solution and the optimized solution.
4. Implement the Solution Gradually
Write the solution in small, understandable steps. Use descriptive variable names and explain important decisions while coding.
Avoid staying silent for several minutes.
5. Test the Code
Test at least:
The provided example
Empty or minimal input
A boundary case
Duplicate values
A case where no result exists
6. Explain Complexity
Always state both time and space complexity.
For example:
We visit every record once, so the time complexity is O(n). The Map may store up to n entries, so the space complexity is also O(n).
Question Style 1: Arrays, Maps, and Sets
Many interview questions require grouping records, counting values, removing duplicates, or checking whether something was previously seen.
JavaScript’s Map and Set are especially useful for these problems.
Example: Users Who Completed Every Required Course
You receive a list of course-completion records:
const completions = [
["Alaa", "JavaScript"],
["Sara", "JavaScript"],
["Alaa", "React"],
["Omar", "JavaScript"],
["Alaa", "Laravel"],
["Sara", "React"],
["Sara", "Laravel"],
["Sara", "React"]
];
const requiredCourses = [
"JavaScript",
"React",
"Laravel"
];
Return the users who completed every required course.
Expected result:
["Alaa", "Sara"]
Notice that Sara’s React completion appears twice. The duplicate should not affect the result.
Approach
Create a Map where:
The key is the user’s name.
The value is a
Setof required courses completed by that user.
A Set is useful because it automatically ignores duplicate values.
Solution
function usersCompletingAllCourses(
completions,
requiredCourses
) {
const required = new Set(requiredCourses);
const coursesByUser = new Map();
for (const [user, course] of completions) {
if (!coursesByUser.has(user)) {
coursesByUser.set(user, new Set());
}
if (required.has(course)) {
coursesByUser.get(user).add(course);
}
}
const result = [];
for (const [user, completedCourses] of coursesByUser) {
if (completedCourses.size === required.size) {
result.push(user);
}
}
return result;
}
Complexity
If there are c completion records and r required courses:
Time complexity: O(c + r)
Space complexity: O(c + r)
Likely Follow-Up
The interviewer might ask:
What if each user must complete every course at least twice?
In that case, a Set would no longer be sufficient because it does not store counts. You would use a nested Map:
Map<user, Map<course, count>>
This is a typical Karat-style progression: the first requirement tests basic grouping, while the follow-up requires changing the data structure.
Question Style 2: Timestamps and Sliding Windows
Time-based problems often require:
Converting timestamps
Sorting records
Finding events inside a time window
Handling inclusive and exclusive boundaries
Example: Detect Frequent Badge Usage
Given employee badge records in HHMM format, return employees who used their badges at least three times within any 60-minute period.
const records = [
["Paul", "1315"],
["Jennifer", "1910"],
["Paul", "1355"],
["Paul", "1405"],
["Jennifer", "2005"],
["Jennifer", "2100"]
];
Expected result:
{
Paul: ["1315", "1355", "1405"]
}
Assume that exactly 60 minutes is included.
Approach
The solution has four main steps:
Group timestamps by employee.
Convert each timestamp into minutes after midnight.
Sort each employee’s timestamps.
Use a sliding window to find three or more records within 60 minutes.
Solution
function toMinutes(time) {
const hours = Number(time.slice(0, 2));
const minutes = Number(time.slice(2));
return hours * 60 + minutes;
}
function findFrequentBadgeUse(records) {
const timesByEmployee = new Map();
for (const [employee, time] of records) {
if (!timesByEmployee.has(employee)) {
timesByEmployee.set(employee, []);
}
timesByEmployee.get(employee).push({
original: time,
minutes: toMinutes(time)
});
}
const result = {};
for (const [employee, times] of timesByEmployee) {
times.sort((a, b) => a.minutes - b.minutes);
let left = 0;
for (let right = 0; right < times.length; right++) {
while (
times[right].minutes -
times[left].minutes >
60
) {
left++;
}
const windowSize = right - left + 1;
if (windowSize >= 3) {
result[employee] = times
.slice(left, right + 1)
.map(entry => entry.original);
break;
}
}
}
return result;
}
How the Sliding Window Works
The left and right variables represent the current time window.
When the difference between the newest and oldest event becomes greater than 60 minutes, we move left forward until the window is valid again.
This avoids comparing every timestamp with every other timestamp.
Complexity
For n records:
Time complexity: O(n log n), primarily because of sorting
Space complexity: O(n)
Common Mistake
Be careful with the boundary condition.
This requirement includes exactly 60 minutes, so the window becomes invalid only when:
difference > 60
Using difference >= 60 would incorrectly reject records exactly 60 minutes apart.
Likely Follow-Ups
The interviewer may ask you to:
Support records crossing midnight
Return every qualifying window
Process incoming records as a stream
Return only the earliest qualifying window
Handle invalid timestamps
Question Style 3: Two-Dimensional Grids
Grid questions test:
Nested loops
Row and column indexes
Boundary conditions
Breadth-first search
Depth-first search
Example: Find a Rectangular Blocked Area
A two-dimensional grid represents a warehouse floor:
1means the position is available.0means the position is blocked.All zeros form exactly one solid rectangle.
Return the rectangle’s top row, left column, width, and height.
const grid = [
[1, 1, 1, 1, 1, 1],
[1, 1, 0, 0, 0, 1],
[1, 1, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1]
];
Expected result:
{
topRow: 1,
leftColumn: 2,
width: 3,
height: 2
}
Approach
Because the zeros form one solid rectangle:
Scan the grid until you find the first zero.
The first zero is the rectangle’s top-left corner.
Move right to calculate the width.
Move down to calculate the height.
Solution
function findBlockedArea(grid) {
if (
grid.length === 0 ||
grid[0].length === 0
) {
return null;
}
const numberOfRows = grid.length;
const numberOfColumns = grid[0].length;
let topRow = -1;
let leftColumn = -1;
for (let row = 0; row < numberOfRows; row++) {
for (
let column = 0;
column < numberOfColumns;
column++
) {
if (grid[row][column] === 0) {
topRow = row;
leftColumn = column;
break;
}
}
if (topRow !== -1) {
break;
}
}
if (topRow === -1) {
return null;
}
let width = 0;
while (
leftColumn + width < numberOfColumns &&
grid[topRow][leftColumn + width] === 0
) {
width++;
}
let height = 0;
while (
topRow + height < numberOfRows &&
grid[topRow + height][leftColumn] === 0
) {
height++;
}
return {
topRow,
leftColumn,
width,
height
};
}
Understanding the Width Calculation
Starting from the top-left corner, we move horizontally:
grid[topRow][leftColumn + width]
For this row:
0 0 0
The width is three.
Understanding the Height Calculation
Starting from the top-left corner, we move vertically:
grid[topRow + height][leftColumn]
For this column:
0
0
The height is two.
Complexity
Time complexity: O(rows × columns)
Space complexity: O(1)
Likely Follow-Up: Multiple Irregular Shapes
The interviewer may change the requirement:
The grid can contain multiple blocked areas, and they are not necessarily rectangles.
This requires BFS or DFS to find connected components.
function findBlockedComponents(grid) {
const rows = grid.length;
const columns = grid[0].length;
const visited = Array.from(
{ length: rows },
() => Array(columns).fill(false)
);
const directions = [
[1, 0],
[-1, 0],
[0, 1],
[0, -1]
];
const components = [];
for (let startRow = 0; startRow < rows; startRow++) {
for (
let startColumn = 0;
startColumn < columns;
startColumn++
) {
const isBlocked =
grid[startRow][startColumn] === 0;
if (
!isBlocked ||
visited[startRow][startColumn]
) {
continue;
}
const queue = [[startRow, startColumn]];
const cells = [];
visited[startRow][startColumn] = true;
let queueIndex = 0;
while (queueIndex < queue.length) {
const [row, column] =
queue[queueIndex++];
cells.push([row, column]);
for (
const [rowChange, columnChange]
of directions
) {
const nextRow = row + rowChange;
const nextColumn =
column + columnChange;
const isInside =
nextRow >= 0 &&
nextRow < rows &&
nextColumn >= 0 &&
nextColumn < columns;
if (
isInside &&
grid[nextRow][nextColumn] === 0 &&
!visited[nextRow][nextColumn]
) {
visited[nextRow][nextColumn] =
true;
queue.push([
nextRow,
nextColumn
]);
}
}
}
components.push(cells);
}
}
return components;
}
The complexity remains:
Time complexity: O(rows × columns)
Space complexity: O(rows × columns)
Question Style 4: Graphs and Relationships
Graph problems frequently appear as real-world relationships rather than mathematical graphs.
Examples include:
Service dependencies
Social connections
Course prerequisites
Folder hierarchies
Employee management structures
Package dependencies
Example: Find Services Affected by a Failure
Each pair contains:
[service, dependency]
For example:
const dependencies = [
["Checkout", "Payments"],
["Orders", "Database"],
["Checkout", "Orders"],
["Reports", "Database"],
["Dashboard", "Reports"]
];
If the Database service fails, return every service affected directly or indirectly.
Expected result:
[
"Orders",
"Reports",
"Checkout",
"Dashboard"
]
The result order does not matter.
Approach
The input tells us what each service depends on:
Orders depends on Database
However, we need to travel in the opposite direction:
Database affects Orders
Therefore, we create a reverse adjacency list:
Database → Orders, Reports
Orders → Checkout
Reports → Dashboard
We can then use BFS to visit all affected services.
Solution
function findAffectedServices(
dependencies,
failedService
) {
const dependents = new Map();
for (const [service, dependency] of dependencies) {
if (!dependents.has(dependency)) {
dependents.set(dependency, []);
}
dependents.get(dependency).push(service);
}
const affected = [];
const visited = new Set([failedService]);
const queue = [failedService];
let queueIndex = 0;
while (queueIndex < queue.length) {
const current = queue[queueIndex++];
const directlyAffected =
dependents.get(current) ?? [];
for (const service of directlyAffected) {
if (visited.has(service)) {
continue;
}
visited.add(seFeatured Articles

Kafka in Production: Partition Strategy, Consumer Lag, Reliability, and an Incident Playbook
A production Kafka cluster needs more than brokers. Learn to choose partition keys, plan retention and capacity, monitor lag, handle rebalances, and rehearse failure recovery.

Laravel and Kafka Without Lost Events: The Transactional Outbox, Idempotent Consumers, and PostgreSQL
A database commit and a Kafka publish cannot safely be treated as one ordinary transaction. This guide builds an outbox and consumer design that survives crashes and duplicate delivery.

Apache Kafka Explained: Topics, Partitions, Consumer Groups, and Your First Event Pipeline
Follow one order event from producer to consumers, then run a local Kafka topic and learn what partitions, offsets, keys, and consumer groups actually do.
Comments
0 commentsNo approved comments are visible yet. New community replies may wait for moderation.