- Published on
16 JavaScript Patterns for Technical Interviews
- Authors

- Name
- Ronald Luo, MSc
16 JavaScript Patterns for Technical Interviews
16 patterns to have on autopilot before your next technical interview.
#1: Check If Key Exists in Object
Scenario: Building a frequency counter, checking if you've visited a node in a graph. You need to know if a key is already in your object/map before accessing it.
Naive:
if (obj[key] !== undefined) {
// breaks if the value is legitimately undefined, 0, or null
}
Better:
// plain object
if ('key' in obj) // checks prototype chain too
if (obj.hasOwnProperty(key)) // own properties only
// Map (preferred in interviews)
if (map.has(key))
Rule: Default to Map with .has(): unambiguous, no prototype weirdness, keys can be any type.
Practice: Two Sum, Contains Duplicate, Longest Consecutive Sequence
#2: Infinity / -Infinity
Scenario: Tracking a running min or max (min cost path, max profit, initializing Dijkstra's distance table). You need a starting value that any real number will beat.
Naive:
let min = 9999999;
let max = -9999999;
// fragile: what if input exceeds your magic number?
Better:
let min = Infinity;
let max = -Infinity;
Gotcha: Math.min() with no arguments returns Infinity. Math.max() with no arguments returns -Infinity. That's the identity element for each operation: not a bug.
Practice: Best Time to Buy and Sell Stock, Maximum Subarray, Network Delay Time
#3: Integer Division (Floor)
Scenario: Binary search midpoint, dividing elements into buckets, any index math. The result must be a whole number, not a float.
Naive:
let mid = (left + right) / 2;
// mid could be 3.5, arr[3.5] is undefined
Better:
let mid = Math.floor((left + right) / 2);
// bitwise shortcut (positive numbers only)
let mid = (left + right) >> 1;
Note: Python has // for floor division. JS has no equivalent operator: you always need Math.floor or the bitwise trick.
Practice: Binary Search, Search in Rotated Sorted Array, Median of Two Sorted Arrays
#4: Swap Two Variables
Scenario: Reversing an array in place, partitioning in quicksort, two-pointer problems like moving zeroes or Dutch national flag.
Naive:
let temp = a;
a = b;
b = temp;
Better:
[a, b] = [b, a];
// works for array elements too
[arr[i], arr[j]] = [arr[j], arr[i]];
Practice: Reverse String, Sort Colors, Kth Largest Element in an Array
#5: Default Dict / Counter
Scenario: Counting character frequencies for anagram check, counting word occurrences, building an adjacency list. Key might not exist yet.
Naive:
let counts = {};
for (let ch of str) {
if (counts[ch] === undefined) {
counts[ch] = 0;
}
counts[ch]++;
}
Better:
let map = new Map();
for (let ch of str) {
map.set(ch, (map.get(ch) || 0) + 1);
}
Gotcha: || 0 fails if the legitimate value could be 0. In counting contexts this never matters, but for other use cases use ?? 0 (nullish coalescing): only triggers on undefined or null.
Practice: Valid Anagram, Group Anagrams, Course Schedule
#6: Initialize 2D Array
Scenario: DP tables (longest common subsequence, edit distance, unique paths), BFS visited matrix, any 2D grid initialized to a default value.
Naive (THE TRAP):
let grid = Array(3).fill(Array(3).fill(0));
grid[0][1] = 5;
// [[0,5,0], [0,5,0], [0,5,0]]: every row changed!
// every row is the same reference
Better:
let dp = Array.from({length: m}, () => Array(n).fill(0));
The arrow function runs fresh for each row: each one is a unique array. No shared references.
This is the #1 silent bug killer in DP problems. If you take one thing from this entire list, it's this.
Practice: Unique Paths, Edit Distance, Longest Common Subsequence
#7: Check If Array Is Empty
Scenario: Popping from a stack, processing a BFS queue, guarding against operating on an empty array.
Naive:
if (arr.length === 0) { /* empty */ }
if (arr.length > 0) { /* not empty */ }
Better:
if (!arr.length) { /* empty */ }
if (arr.length) { /* not empty */ }
// BFS loop
while (queue.length) {
let node = queue.shift();
}
arr.length is 0 when empty, which is falsy. That's it.
Practice: Valid Parentheses, Binary Tree Level Order Traversal, Evaluate Reverse Polish Notation
#8: Get Last Element
Scenario: Using an array as a stack, checking the top before push/pop, comparing against the most recent result (like in merge intervals).
Naive:
let last = arr[arr.length - 1];
Better:
let last = arr.at(-1);
Negative indexing, just like Python. -1 is last, -2 is second to last.
Gotcha: .at() is read-only for assignment. You can't do arr.at(-1) = 5. For writing to a primitive position, you still need arr[arr.length - 1] = 5. But you CAN mutate through it: arr.at(-1)[1] = value works because it returns the reference.
Practice: Merge Intervals, Min Stack, Daily Temperatures
#9: String to Char Array
Scenario: Reversing a string, rearranging characters, any in-place transformation. JS strings are immutable, so you can't do str[i] = 'x'.
Naive:
let reversed = '';
for (let i = str.length - 1; i >= 0; i--) {
reversed += str[i];
}
// string concatenation in a loop is O(n²) worst case
Better:
// string to array
let chars = str.split('');
// manipulate
chars[0] = 'X';
[chars[1], chars[2]] = [chars[2], chars[1]];
// array back to string
let result = chars.join('');
// classic reverse one-liner
let reversed = str.split('').reverse().join('');
Gotcha: split('') breaks on emojis and multi-byte unicode. Use [...str] instead if unicode could matter. Mentioning this unprompted scores points.
Practice: Reverse String, Reorganize String, Reverse Words in a String
#10: Character Code Math
Scenario: Mapping characters to array indices (frequency bucket for lowercase letters, anagram check with a fixed-size array, group anagrams with an O(M) key instead of O(M log M) sort).
Naive:
// using a Map when the problem says "lowercase English letters only"
let map = new Map();
for (let ch of str) {
map.set(ch, (map.get(ch) || 0) + 1);
}
Better:
const START = 'a'.charCodeAt(0); // don't memorize 97
const SIZE = 'z'.charCodeAt(0) - START + 1; // don't memorize 26
let counts = Array(SIZE).fill(0);
for (let ch of str) {
counts[ch.charCodeAt(0) - START]++;
}
// use as a hash key for group anagrams
let key = counts.join(',');
How it works: ASCII assigns every character a sequential number. a=97 through z=122. Subtracting START shifts them to 0-25. Each letter maps to an array index. Same letters always produce the same frequency fingerprint.
Adapting to different ranges:
- Uppercase (A-Z):
START = 'A'.charCodeAt(0),SIZE = 'Z'.charCodeAt(0) - START + 1 - Digits (0-9):
START = '0'.charCodeAt(0),SIZE = '9'.charCodeAt(0) - START + 1 - Mixed ranges: Just use a Map: ASCII ranges aren't contiguous across groups.
Key insight: The pattern is always START = first char's code, SIZE = last - first + 1. Derive everything from the characters. Zero memorization.
Practice: Group Anagrams, Valid Anagram, First Unique Character in a String
#11: Set Operations
Scenario: Detecting duplicates, checking if a complement exists, tracking visited nodes in BFS/DFS, finding intersection of two arrays.
Naive:
// O(n) lookup each time: total O(n²)
let seen = [];
for (let num of arr) {
if (seen.includes(num)) return true;
seen.push(num);
}
Better:
// O(1) lookup: total O(n)
let seen = new Set();
for (let num of arr) {
if (seen.has(num)) return true;
seen.add(num);
}
// one-liner dedupe
let unique = [...new Set(arr)];
// intersection
let setA = new Set(arrA);
let intersection = arrB.filter(x => setA.has(x));
Mental rule:
- "Have I seen this before?" → Set
- "Have I seen this and what was the value?" → Map
- Plain object
{}→ avoid in interviews. Keys coerce to strings, soobj[1]andobj['1']are the same key. Map doesn't have that problem.
Practice: Contains Duplicate, Two Sum, Clone Graph
#12: Min/Max of Array
Scenario: Finding the smallest/largest value (min cost, max profit, range of values, validating bounds).
Naive:
let min = arr[0];
for (let i = 1; i < arr.length; i++) {
if (arr[i] < min) min = arr[i];
}
Better:
let min = Math.min(...arr);
let max = Math.max(...arr);
Gotcha: Spread puts every element on the call stack. 100k+ elements will stack overflow. For huge arrays:
let min = arr.reduce((a, b) => Math.min(a, b), Infinity);
In an interview, mention the limitation if the interviewer says "millions of elements." Otherwise spread is fine.
Practice: Maximum Subarray, Maximum Product Subarray, Maximum Product of Three Numbers
#13: Clone an Array
Scenario: Sorting without modifying the original, snapshotting a path in backtracking, building the next DP row from the current one.
Naive:
let copy = [];
for (let i = 0; i < arr.length; i++) {
copy.push(arr[i]);
}
Better:
let copy = [...arr];
// or
let copy = arr.slice();
The trap: shallow clone only. For 2D arrays:
let grid = [[1,2],[3,4]];
let copy = [...grid];
copy[0][0] = 999;
console.log(grid[0][0]); // 999: original mutated!
// deep clone for 2D
let copy = grid.map(row => [...row]);
Backtracking killer:
// WRONG: pushes the reference, path keeps mutating
result.push(path);
// RIGHT: snapshots the current state
result.push([...path]);
If your permutations answer has every entry identical, this is why.
Practice: Permutations, Subsets, Sort an Array
#14: Priority Queue / MinHeap
Scenario: Merge K sorted lists, Kth largest element, Dijkstra's shortest path, task scheduling. Repeatedly need the smallest/largest from a dynamic collection.
Naive:
arr.push(newVal);
arr.sort((a, b) => a - b); // O(n log n) every insertion
let min = arr.shift();
Better (memorize this):
class MinHeap {
constructor() { this.data = []; }
size() { return this.data.length; }
push(val) {
this.data.push(val);
this._bubbleUp(this.data.length - 1);
}
pop() {
let top = this.data[0];
let last = this.data.pop();
if (this.data.length) {
this.data[0] = last;
this._sinkDown(0);
}
return top;
}
_bubbleUp(i) {
while (i > 0) {
let parent = (i - 1) >> 1;
if (this.data[i] >= this.data[parent]) break;
[this.data[i], this.data[parent]] = [this.data[parent], this.data[i]];
i = parent;
}
}
_sinkDown(i) {
while (true) {
let smallest = i;
let left = 2 * i + 1;
let right = 2 * i + 2;
if (left < this.data.length && this.data[left] < this.data[smallest]) smallest = left;
if (right < this.data.length && this.data[right] < this.data[smallest]) smallest = right;
if (smallest === i) break;
[this.data[i], this.data[smallest]] = [this.data[smallest], this.data[i]];
i = smallest;
}
}
}
This is the one item you must memorize and drill. Python has heapq built in. JS has nothing. A heap problem in JS means 5-7 minutes of infrastructure before you start solving. Practice until you can write it in under 3 minutes.
Practice: Merge k Sorted Lists, Kth Largest Element in an Array, Network Delay Time
#15: Iterate a Map in Insertion Order
Scenario: Returning grouped anagrams, finding the first non-repeating character, any problem where the order you inserted matters.
Naive (plain object):
let obj = {};
obj['b'] = 2;
obj['a'] = 1;
for (let key in obj) {
console.log(key, obj[key]);
}
// integer-like keys get sorted numerically first
// 'for...in' traverses the prototype chain
Better:
let map = new Map();
map.set('b', 2);
map.set('a', 1);
for (let [key, value] of map) {
console.log(key, value);
}
// always b, a: insertion order guaranteed
Rule: Map always iterates in insertion order. No surprises, no prototype chain, no numeric key reordering. Another reason to default to Map over {}.
Practice: Group Anagrams, First Unique Character in a String, LRU Cache
#16: True Modulo for Negative Numbers
Scenario: Circular array problems like rotating an array, wrapping around a circular buffer, Caesar cipher with left shift, navigating circular indices. Any time subtraction could make an index go negative.
Naive:
let index = (i - k) % n;
// i=1, k=3, n=5
// (1 - 3) % 5 = -2 % 5 = -2 ← wrong, wanted 3
// arr[-2] is undefined
JS % is a remainder, not a true modulo. It keeps the sign of the left operand. Python's % returns the correct positive result natively.
Better:
let index = ((i - k) % n + n) % n;
// ((-2) % 5 + 5) % 5
// (-2 + 5) % 5
// 3 % 5
// 3 ← correct
The + n shifts any negative remainder into positive territory. The outer % n handles cases where the result was already positive so + n doesn't overshoot.
Every circular array/rotation problem is a landmine without this.
Practice: Rotate Array, Maximum Sum Circular Subarray, Next Greater Element II