Learn JavaScript data structures: Arrays, Objects, Maps, Sets, Stacks, Queues, and Linked Lists. Know when to use each one.
Why does finding an item in an array take longer as it grows? Why can you look up an object property instantly regardless of how many properties it has? The answer lies in data structures.
Copy
Ask AI
// Array: searching gets slower as the array growsconst users = ['alice', 'bob', 'charlie', /* ...thousands more */]users.includes('zara') // Has to check every element - O(n)// Object: lookup is instant regardless of sizeconst userMap = { alice: 1, bob: 2, charlie: 3, /* ...thousands more */ }userMap['zara'] // Direct access - O(1)
A data structure is a way of organizing data so it can be used efficiently. As Donald Knuth emphasizes in The Art of Computer Programming, the choice of data structure often matters more than the choice of algorithm. The right structure makes your code faster and cleaner. The wrong one can make simple operations painfully slow.
How to implement: Stack, Queue, Linked List, Binary Search Tree
Choosing the right data structure for the job
Common interview questions and patterns
Prerequisites: This guide shows time complexity (like O(1) and O(n)) for operations. If you’re not familiar with Big O notation, check out our Algorithms & Big O guide first. We also use classes for implementations.
Think of data structures like different ways to organize a library. You could:
Stack books on a table — Easy to add/remove from the top, but finding a specific book means digging through the pile
Line them up on a shelf — Easy to browse in order, but adding a book in the middle means shifting everything
Organize by category with an index — Finding any book is fast, but you need to maintain the index
Copy
Ask AI
┌─────────────────────────────────────────────────────────────────────────┐│ DATA STRUCTURE TRADE-OFFS │├─────────────────────────────────────────────────────────────────────────┤│ ││ ARRAY OBJECT/MAP LINKED LIST ││ ┌─┬─┬─┬─┬─┐ ┌────────────┐ ┌───┐ ┌───┐ ││ │0│1│2│3│4│ │ key: value │ │ A │──►│ B │──► ││ └─┴─┴─┴─┴─┘ │ key: value │ └───┘ └───┘ ││ └────────────┘ ││ ✓ Fast index access ✓ Fast key lookup ✓ Fast insert/delete ││ ✓ Ordered ✓ Flexible keys ✗ Slow search ││ ✗ Slow insert in middle ✗ No order (Object) ✗ No index access ││ │└─────────────────────────────────────────────────────────────────────────┘
Every data structure has trade-offs. Your job is to pick the one that makes your most frequent operations fast.
An Object stores key-value pairs where keys are strings or Symbols. It’s JavaScript’s fundamental way to group related data.
Copy
Ask AI
const user = { name: 'Alice', age: 30, email: '[email protected]'}// Access - O(1)user.name // 'Alice'user['age'] // 30// Add/Update - O(1)user.role = 'admin'// Delete - O(1)delete user.email// Check if key exists - O(1)'name' in user // trueuser.hasOwnProperty('name') // true
Limitations of Objects:
Keys are converted to strings (numbers become “1”, “2”, etc.)
Objects have a prototype chain (inherited properties)
No built-in .size property
As defined in the ECMAScript specification, property order is preserved in ES2015+, but with specific rules: integer keys are sorted numerically first, then string keys appear in insertion order
These methods are part of ES2024 and are supported in all modern browsers as of late 2024. Check browser compatibility if you need to support older browsers.
Copy
Ask AI
const a = new Set([1, 2, 3])const b = new Set([2, 3, 4])// Union: elements in either seta.union(b) // Set {1, 2, 3, 4}// Intersection: elements in both setsa.intersection(b) // Set {2, 3}// Difference: elements in a but not in ba.difference(b) // Set {1}// Symmetric difference: elements in either but not botha.symmetricDifference(b) // Set {1, 4}// Subset checknew Set([1, 2]).isSubsetOf(a) // true
WeakMap and WeakSet are special versions where keys (WeakMap) or values (WeakSet) are held “weakly.” This means they don’t prevent garbage collection.WeakMap:
Keys must be objects (or non-registered symbols)
If the key object has no other references, it gets garbage collected
Not iterable (no .keys(), .values(), .forEach())
No .size property
Copy
Ask AI
const privateData = new WeakMap()class User { constructor(name, password) { this.name = name // Store private data that can't be accessed externally privateData.set(this, { password }) } checkPassword(input) { return privateData.get(this).password === input }}const user = new User('Alice', 'secret123')user.name // 'Alice'user.password // undefined - it's private!user.checkPassword('secret123') // true// When 'user' is garbage collected, the private data is too
WeakSet:
Values must be objects
Useful for tracking which objects have been processed
Copy
Ask AI
const processed = new WeakSet()function processOnce(obj) { if (processed.has(obj)) { return // Already processed } processed.add(obj) // Do expensive processing...}
When to use Weak versions:
Caching computed data for objects
Storing private instance data
Tracking objects without preventing garbage collection
A Queue follows First-In-First-Out: the first item added is the first removed. Think of a line at a store. According to the Stack Overflow Developer Survey 2023, queues and other fundamental data structures remain among the most commonly tested topics in technical interviews.
Copy
Ask AI
┌─────────────────────────────────────────────────────────────────────────┐│ QUEUE (FIFO) │├─────────────────────────────────────────────────────────────────────────┤│ ││ enqueue(4) dequeue() ││ │ │ ││ ▼ ▼ ││ ┌───┬───┬───┬───┐ ┌───┬───┬───┐ ││ │ 4 │ 3 │ 2 │ 1 │ ───────────────────────► │ 4 │ 3 │ 2 │ ││ └───┴───┴───┴───┘ └───┴───┴───┘ ││ back front back front ││ ││ "First in, first out" - like a line at a store ││ │└─────────────────────────────────────────────────────────────────────────┘
Performance note: Using shift() on an array is O(n) because all remaining elements must be re-indexed. For performance-critical code, use a linked list implementation or an object with head/tail pointers.
┌─────────────────────────────────────────────────────────────────────────┐│ WHICH DATA STRUCTURE SHOULD I USE? │├─────────────────────────────────────────────────────────────────────────┤│ ││ Need ordered data with index access? ││ └──► ARRAY ││ ││ Need key-value pairs with string keys? ││ └──► OBJECT (static data) or MAP (dynamic) ││ ││ Need key-value with any type as key? ││ └──► MAP ││ ││ Need unique values only? ││ └──► SET ││ ││ Need LIFO (last in, first out)? ││ └──► STACK ││ ││ Need FIFO (first in, first out)? ││ └──► QUEUE ││ ││ Need fast insert/delete at beginning? ││ └──► LINKED LIST ││ ││ Need fast search + sorted data? ││ └──► BINARY SEARCH TREE ││ ││ Modeling relationships/connections? ││ └──► GRAPH ││ │└─────────────────────────────────────────────────────────────────────────┘
Interview questions often test your understanding of data structures. Here are patterns you’ll encounter:
Array: Two Sum
Problem: Find two numbers in an array that add up to a target.Approach: Use a Map to store numbers you’ve seen. For each number, check if target - number exists in the Map.
Copy
Ask AI
function twoSum(nums, target) { const seen = new Map() for (let i = 0; i < nums.length; i++) { const complement = target - nums[i] if (seen.has(complement)) { return [seen.get(complement), i] } seen.set(nums[i], i) } return []}twoSum([2, 7, 11, 15], 9) // [0, 1]
Why Map? O(1) lookup turns O(n²) brute force into O(n).
Stack: Valid Parentheses
Problem: Check if a string of brackets is valid: ()[]{}.Approach: Push opening brackets onto stack. When you see a closing bracket, pop and check if it matches.
Copy
Ask AI
function isValid(s) { const stack = [] const pairs = { ')': '(', ']': '[', '}': '{' } for (const char of s) { if (char in pairs) { // Closing bracket - check if it matches if (stack.pop() !== pairs[char]) { return false } } else { // Opening bracket - push to stack stack.push(char) } } return stack.length === 0}isValid('([{}])') // trueisValid('([)]') // false
Linked List: Reverse
Problem: Reverse a linked list.Approach: Keep track of previous, current, and next. Reverse pointers as you go.
Copy
Ask AI
function reverseList(head) { let prev = null let current = head while (current) { const next = current.next // Save next current.next = prev // Reverse pointer prev = current // Move prev forward current = next // Move current forward } return prev // New head}
Key insight: You need three pointers to avoid losing references.
Linked List: Detect Cycle
Problem: Determine if a linked list has a cycle.Approach: Floyd’s Tortoise and Hare - use two pointers, one fast (2 steps) and one slow (1 step). If they meet, there’s a cycle.
Copy
Ask AI
function hasCycle(head) { let slow = head let fast = head while (fast && fast.next) { slow = slow.next fast = fast.next.next if (slow === fast) { return true // They met - cycle exists } } return false // Fast reached end - no cycle}
Why this works: In a cycle, the fast pointer will eventually “lap” the slow pointer.
Tree: Maximum Depth
Problem: Find the maximum depth of a binary tree.Approach: Recursively find the depth of left and right subtrees, take the max.
Copy
Ask AI
function maxDepth(root) { if (!root) return 0 const leftDepth = maxDepth(root.left) const rightDepth = maxDepth(root.right) return Math.max(leftDepth, rightDepth) + 1}
Base case: Empty tree has depth 0.
Queue: Implement with Two Stacks
Problem: Implement a queue using only stacks.Approach: Use two stacks. Push to stack1. For dequeue, if stack2 is empty, pour all of stack1 into stack2 (reversing order), then pop from stack2.
Copy
Ask AI
class QueueFromStacks { constructor() { this.stack1 = [] // For enqueue this.stack2 = [] // For dequeue } enqueue(item) { this.stack1.push(item) } dequeue() { if (this.stack2.length === 0) { // Pour stack1 into stack2 while (this.stack1.length) { this.stack2.push(this.stack1.pop()) } } return this.stack2.pop() }}
Amortized O(1): Each element is moved at most twice.
Question 1: When would you use a Map instead of an Object?
Answer:Use Map when:
Keys are not strings (objects, numbers, etc.)
You need to know the size frequently (.size vs Object.keys().length)
You add/delete keys often (Map is optimized for this)
You need guaranteed insertion order
You want to avoid prototype chain issues
Use Object when:
Keys are known strings
You’re working with JSON data
You need object destructuring or spread syntax
Question 2: Why is array shift() O(n) but pop() O(1)?
Answer:pop() removes from the end. No other elements need to move.shift() removes from the beginning. Every remaining element must be re-indexed:
Element at index 1 moves to 0
Element at index 2 moves to 1
…and so on
This is why Queue implementations with arrays have O(n) dequeue. For O(1), use a linked list or object with head/tail pointers.
Question 3: What's the difference between a Stack and a Queue?
Answer:Stack (LIFO): Last In, First Out
Like a stack of plates - you take from the top
push() and pop() operate on the same end
Use for: undo/redo, back button, recursion
Queue (FIFO): First In, First Out
Like a line at a store - first person in line is served first
enqueue() adds to back, dequeue() removes from front
Use for: task scheduling, BFS, print queues
Question 4: When would a Linked List be better than an Array?
Answer:Linked List wins when:
You frequently insert/delete at the beginning (O(1) vs O(n))
You don’t need random access by index
You’re implementing a queue (O(1) dequeue)
Memory is fragmented (nodes can be anywhere)
Array wins when:
You need index-based access
You iterate sequentially often
You mostly add/remove from the end
You need .length, .map(), .filter(), etc.
Question 5: What makes Binary Search Trees fast?
Answer:BSTs use the rule: left < parent < right. This means:
To find a value, compare with root
If smaller, go left; if larger, go right
Each comparison eliminates half the remaining nodes
This gives O(log n) search, insert, and delete (on average).Catch: If you insert sorted data, the tree becomes a linked list (all nodes on one side), and operations become O(n). Self-balancing trees (AVL, Red-Black) solve this.
Question 6: How would you remove duplicates from an array?
Answer:The cleanest way is with Set:
Copy
Ask AI
const unique = [...new Set(array)]
This works because:
new Set(array) creates a Set (which only keeps unique values)
[...set] spreads the Set back into an array
Time complexity: O(n) - each element is processed once.
Data structures are ways of organizing and storing data so it can be accessed and modified efficiently. JavaScript provides several built-in structures — Array, Object, Map, Set, WeakMap, and WeakSet — and you can implement others like stacks, queues, and linked lists using classes. Choosing the right structure depends on your most frequent operations.
When should I use a Map instead of an Object in JavaScript?
Use a Map when your keys are not strings (objects, functions, numbers), when you need guaranteed insertion order, when you frequently add and delete keys, or when you need the .size property. MDN recommends Map over Object for frequent additions and removals of key-value pairs due to better performance characteristics.
Are JavaScript arrays actually arrays?
Not in the traditional computer science sense. As the V8 blog explains, JavaScript engines use multiple internal representations. Dense arrays with sequential numeric keys are stored as contiguous memory (like C arrays), but sparse arrays or arrays with mixed types may be stored as hash tables internally. This is why V8 optimizes differently based on how you use arrays.
How do I choose the right data structure for my problem?
Consider your most frequent operations. If you need fast lookups by key, use a Map or Object. If you need to track unique values, use a Set. If order matters and you access by index, use an Array. If you need fast insertion and deletion at both ends, consider a linked list or deque implementation.
What is the time complexity of common JavaScript data structure operations?
Array access by index is O(1), but indexOf() is O(n). Object, Map, and Set lookups are all O(1). Array push()/pop() are O(1), but shift()/unshift() are O(n) because all elements must be re-indexed. Understanding these complexities, as outlined in the ECMAScript specification, helps you avoid performance bottlenecks.