frontend / javascript core / data-structures.md

JavaScript Data Structures Interview Questions & Answers

4 interview angles 12 min read source

JavaScript Data Structures Interview Questions & Answers

Data structures are fundamental building blocks for organizing and storing data efficiently. JavaScript provides several built-in data structures that are essential for modern web development.

Table of Contents


Objects

1. What are objects in JavaScript and how do you create them?

Objects are collections of key-value pairs where keys are strings (or symbols) and values can be any data type.

Ways to create objects:

// 1. Object literal (most common)
const person = {
    name: "John",
    age: 30,
    greet() {
        return `Hello, I'm ${this.name}`;
    }
};

// 2. Constructor function
function Person(name, age) {
    this.name = name;
    this.age = age;
}
const person2 = new Person("Jane", 25);

// 3. Object.create()
const person3 = Object.create(null);
person3.name = "Bob";

// 4. Class (ES6+)
class PersonClass {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }

    greet() {
        return `Hello, I'm ${this.name}`;
    }
}
const person4 = new PersonClass("Alice", 28);

// 5. Factory function
function createPerson(name, age) {
    return {
        name,
        age,
        greet() {
            return `Hello, I'm ${this.name}`;
        }
    };
}
const person5 = createPerson("Charlie", 35);

2. How do you access and modify object properties?

Accessing properties:

const person = {
    name: "John",
    age: 30,
    "favorite-color": "blue" // property with special characters
};

// Dot notation
console.log(person.name);           // "John"
console.log(person.age);            // 30

// Bracket notation
console.log(person["name"]);        // "John"
console.log(person["favorite-color"]); // "blue"

// Dynamic property access
const propertyName = "age";
console.log(person[propertyName]);  // 30

Modifying properties:

const person = { name: "John", age: 30 };

// Adding properties
person.city = "New York";
person["country"] = "USA";

// Modifying properties
person.age = 31;
person["name"] = "Johnny";

// Deleting properties
delete person.age;
delete person["city"];

console.log(person); // { name: "Johnny", country: "USA" }

3. What are object methods and how do you use them?

Object methods are functions that are properties of objects.

const calculator = {
    add(a, b) {
        return a + b;
    },

    subtract: function(a, b) {
        return a - b;
    },

    multiply(a, b) {
        return a * b;
    },

    divide: (a, b) => a / b
};

console.log(calculator.add(5, 3));      // 8
console.log(calculator.subtract(10, 4)); // 6
console.log(calculator.multiply(2, 6));  // 12
console.log(calculator.divide(15, 3));   // 5

Using this in methods:

const user = {
    name: "John",
    age: 30,

    greet() {
        return `Hello, I'm ${this.name}`;
    },

    birthday() {
        this.age++;
        return `Happy birthday! You are now ${this.age}`;
    }
};

console.log(user.greet());     // "Hello, I'm John"
console.log(user.birthday());  // "Happy birthday! You are now 31"

4. What are getters and setters in objects?

Getters and setters allow you to define computed properties and control access to object properties.

const circle = {
    radius: 5,

    // Getter for diameter
    get diameter() {
        return this.radius * 2;
    },

    // Setter for diameter
    set diameter(value) {
        this.radius = value / 2;
    },

    // Getter for area
    get area() {
        return Math.PI * this.radius ** 2;
    }
};

console.log(circle.radius);    // 5
console.log(circle.diameter);  // 10 (computed)
console.log(circle.area);      // 78.54... (computed)

circle.diameter = 20;          // Sets radius to 10
console.log(circle.radius);    // 10

5. How do you iterate over object properties?

Different ways to iterate over objects:

const person = {
    name: "John",
    age: 30,
    city: "New York"
};

// 1. for...in loop (iterates over enumerable properties)
for (let key in person) {
    console.log(`${key}: ${person[key]}`);
}

// 2. Object.keys()
Object.keys(person).forEach(key => {
    console.log(`${key}: ${person[key]}`);
});

// 3. Object.values()
Object.values(person).forEach(value => {
    console.log(value);
});

// 4. Object.entries()
Object.entries(person).forEach(([key, value]) => {
    console.log(`${key}: ${value}`);
});

// 5. Object.getOwnPropertyNames()
Object.getOwnPropertyNames(person).forEach(key => {
    console.log(`${key}: ${person[key]}`);
});

Arrays

6. What are arrays and how do you create them?

Arrays are ordered collections of values that can be of any type.

Ways to create arrays:

// 1. Array literal (most common)
const arr1 = [1, 2, 3, 4, 5];
const arr2 = ["apple", "banana", "orange"];
const arr3 = [1, "hello", true, { name: "John" }];

// 2. Array constructor
const arr4 = new Array(1, 2, 3, 4, 5);
const arr5 = new Array(5); // Creates array with 5 empty slots

// 3. Array.from()
const arr6 = Array.from("hello"); // ["h", "e", "l", "l", "o"]
const arr7 = Array.from({ length: 5 }, (_, i) => i); // [0, 1, 2, 3, 4]

// 4. Spread operator
const arr8 = [...arr1, 6, 7, 8];

// 5. Array.of()
const arr9 = Array.of(1, 2, 3, 4, 5);

7. How do you access and modify array elements?

Accessing elements:

const arr = ["apple", "banana", "orange", "grape"];

// Access by index
console.log(arr[0]);     // "apple"
console.log(arr[2]);     // "orange"
console.log(arr[-1]);    // undefined (no negative indexing)

// Access last element
console.log(arr[arr.length - 1]); // "grape"

// Check if index exists
console.log(arr.hasOwnProperty(1)); // true
console.log(arr.hasOwnProperty(10)); // false

Modifying elements:

const arr = ["apple", "banana", "orange"];

// Modify existing element
arr[1] = "pear";
console.log(arr); // ["apple", "pear", "orange"]

// Add element at end
arr.push("grape");
console.log(arr); // ["apple", "pear", "orange", "grape"]

// Remove last element
const lastElement = arr.pop();
console.log(lastElement); // "grape"
console.log(arr); // ["apple", "pear", "orange"]

// Add element at beginning
arr.unshift("kiwi");
console.log(arr); // ["kiwi", "apple", "pear", "orange"]

// Remove first element
const firstElement = arr.shift();
console.log(firstElement); // "kiwi"
console.log(arr); // ["apple", "pear", "orange"]

8. What are sparse arrays and how do they work?

Sparse arrays are arrays with gaps (undefined elements).

// Creating sparse arrays
const sparse1 = [1, , 3, , 5]; // Gaps at indices 1 and 3
const sparse2 = new Array(5);  // Creates array with 5 undefined elements

console.log(sparse1.length);   // 5
console.log(sparse1[1]);       // undefined
console.log(sparse1[2]);       // 3

// Checking for gaps
console.log(sparse1.hasOwnProperty(1)); // false
console.log(sparse1.hasOwnProperty(2)); // true

// Iterating over sparse arrays
sparse1.forEach((item, index) => {
    console.log(`Index ${index}: ${item}`);
}); // Only logs defined elements

// Using for...of (skips undefined)
for (let item of sparse1) {
    console.log(item);
}

// Using for...in (includes all indices)
for (let index in sparse1) {
    console.log(`Index ${index}: ${sparse1[index]}`);
}

9. How do you work with array-like objects?

Array-like objects have numeric indices and a length property but aren’t arrays.

// Common array-like objects
const string = "hello";
const arguments = (function() { return arguments; })("a", "b", "c");
const nodeList = document.querySelectorAll("div");

// Converting to arrays
const arr1 = Array.from(string);           // ["h", "e", "l", "l", "o"]
const arr2 = Array.from(arguments);        // ["a", "b", "c"]
const arr3 = Array.from(nodeList);         // Array of DOM elements

// Using spread operator
const arr4 = [...string];                  // ["h", "e", "l", "l", "o"]
const arr5 = [...arguments];               // ["a", "b", "c"]

// Using slice
const arr6 = Array.prototype.slice.call(string); // ["h", "e", "l", "l", "o"]

// Creating array-like object
const arrayLike = {
    0: "first",
    1: "second",
    2: "third",
    length: 3
};

const arr7 = Array.from(arrayLike); // ["first", "second", "third"]

Maps

10. What are Maps and when would you use them?

Maps are collections of key-value pairs where keys can be any type (not just strings).

Key differences from objects:

  • Keys can be any type (objects, functions, primitives)
  • Maintains insertion order
  • Built-in size property
  • Better performance for frequent additions/removals
// Creating maps
const map1 = new Map();
const map2 = new Map([
    ["key1", "value1"],
    ["key2", "value2"]
]);

// Adding key-value pairs
map1.set("string", "value");
map1.set(42, "number key");
map1.set({}, "object key");
map1.set(() => {}, "function key");

// Getting values
console.log(map1.get("string"));     // "value"
console.log(map1.get(42));           // "number key"
console.log(map1.get({}));           // undefined (different object)

// Checking for keys
console.log(map1.has("string"));     // true
console.log(map1.has(42));           // true

// Deleting entries
map1.delete("string");

// Getting size
console.log(map1.size);              // 3

// Clearing all entries
map1.clear();
console.log(map1.size);              // 0

11. How do you iterate over Maps?

Different ways to iterate over Maps:

const map = new Map([
    ["name", "John"],
    ["age", 30],
    ["city", "New York"]
]);

// 1. for...of with entries (default)
for (let [key, value] of map) {
    console.log(`${key}: ${value}`);
}

// 2. for...of with entries() method
for (let [key, value] of map.entries()) {
    console.log(`${key}: ${value}`);
}

// 3. for...of with keys()
for (let key of map.keys()) {
    console.log(key);
}

// 4. for...of with values()
for (let value of map.values()) {
    console.log(value);
}

// 5. forEach method
map.forEach((value, key) => {
    console.log(`${key}: ${value}`);
});

// 6. Converting to arrays
const entries = Array.from(map.entries());
const keys = Array.from(map.keys());
const values = Array.from(map.values());

12. What are the differences between Maps and Objects?

Feature Map Object
Key types Any value String, Symbol
Size property Built-in Manual calculation
Iteration Built-in iterable Manual iteration
Performance Better for frequent changes Better for property access
Serialization Not JSON serializable JSON serializable
Prototype No prototype chain Has prototype chain

Example:

// Map example
const map = new Map();
map.set({}, "object key");
map.set(() => {}, "function key");
console.log(map.size); // 2

// Object example
const obj = {};
obj[{}] = "object key";        // Converts to "[object Object]"
obj[() => {}] = "function key"; // Converts to "() => {}"
console.log(Object.keys(obj).length); // 2

Sets

13. What are Sets and when would you use them?

Sets are collections of unique values where each value can occur only once.

Key characteristics:

  • Values must be unique
  • Maintains insertion order
  • Can hold any type of value
  • Built-in size property
// Creating sets
const set1 = new Set();
const set2 = new Set([1, 2, 3, 4, 5]);
const set3 = new Set("hello"); // Creates set of unique characters

// Adding values
set1.add(1);
set1.add("string");
set1.add({ name: "John" });
set1.add(1); // Won't add duplicate

// Checking for values
console.log(set1.has(1));           // true
console.log(set1.has("string"));    // true
console.log(set1.has({ name: "John" })); // false (different object)

// Deleting values
set1.delete(1);

// Getting size
console.log(set1.size);             // 2

// Clearing all values
set1.clear();
console.log(set1.size);             // 0

14. How do you perform set operations?

Common set operations:

const set1 = new Set([1, 2, 3, 4]);
const set2 = new Set([3, 4, 5, 6]);

// Union
const union = new Set([...set1, ...set2]);
console.log(union); // Set {1, 2, 3, 4, 5, 6}

// Intersection
const intersection = new Set(
    [...set1].filter(x => set2.has(x))
);
console.log(intersection); // Set {3, 4}

// Difference (set1 - set2)
const difference = new Set(
    [...set1].filter(x => !set2.has(x))
);
console.log(difference); // Set {1, 2}

// Symmetric difference
const symmetricDifference = new Set([
    ...[...set1].filter(x => !set2.has(x)),
    ...[...set2].filter(x => !set1.has(x))
]);
console.log(symmetricDifference); // Set {1, 2, 5, 6}

// Subset check
const isSubset = (setA, setB) => {
    return [...setA].every(x => setB.has(x));
};
console.log(isSubset(new Set([1, 2]), set1)); // true

15. How do you iterate over Sets?

Different ways to iterate over Sets:

const set = new Set(["apple", "banana", "orange"]);

// 1. for...of (default iteration)
for (let value of set) {
    console.log(value);
}

// 2. forEach method
set.forEach(value => {
    console.log(value);
});

// 3. Converting to array
const array = Array.from(set);
array.forEach(value => {
    console.log(value);
});

// 4. Using spread operator
[...set].forEach(value => {
    console.log(value);
});

// 5. Using entries() (returns [value, value] pairs)
for (let [value] of set.entries()) {
    console.log(value);
}

WeakMap and WeakSet

16. What are WeakMap and WeakSet and when would you use them?

WeakMap and WeakSet are collections that hold weak references to objects, allowing garbage collection when the object is no longer referenced elsewhere.

Key characteristics:

  • Keys/values must be objects
  • No iteration methods
  • No size property
  • Automatic garbage collection
  • Cannot prevent garbage collection
// WeakMap example
const weakMap = new WeakMap();
let obj1 = { name: "John" };
let obj2 = { name: "Jane" };

weakMap.set(obj1, "data1");
weakMap.set(obj2, "data2");

console.log(weakMap.get(obj1)); // "data1"
console.log(weakMap.has(obj1)); // true

obj1 = null; // obj1 can now be garbage collected
// weakMap.get(obj1) would return undefined

// WeakSet example
const weakSet = new WeakSet();
let obj3 = { name: "Bob" };
let obj4 = { name: "Alice" };

weakSet.add(obj3);
weakSet.add(obj4);

console.log(weakSet.has(obj3)); // true

obj3 = null; // obj3 can now be garbage collected
// weakSet.has(obj3) would return false

Use cases:

// WeakMap - storing private data
const privateData = new WeakMap();

class User {
    constructor(name) {
        privateData.set(this, { name });
    }

    getName() {
        return privateData.get(this).name;
    }
}

// WeakSet - tracking visited objects
const visited = new WeakSet();

function processObject(obj) {
    if (visited.has(obj)) {
        return; // Already processed
    }

    visited.add(obj);
    // Process the object...
}

Custom Data Structures

17. How would you implement a Stack?

Stack is a LIFO (Last In, First Out) data structure.

class Stack {
    constructor() {
        this.items = [];
    }

    // Add element to top
    push(element) {
        this.items.push(element);
    }

    // Remove and return top element
    pop() {
        if (this.isEmpty()) {
            return "Stack is empty";
        }
        return this.items.pop();
    }

    // Return top element without removing
    peek() {
        if (this.isEmpty()) {
            return "Stack is empty";
        }
        return this.items[this.items.length - 1];
    }

    // Check if stack is empty
    isEmpty() {
        return this.items.length === 0;
    }

    // Return stack size
    size() {
        return this.items.length;
    }

    // Clear stack
    clear() {
        this.items = [];
    }
}

// Usage
const stack = new Stack();
stack.push(1);
stack.push(2);
stack.push(3);
console.log(stack.pop());  // 3
console.log(stack.peek()); // 2
console.log(stack.size()); // 2

18. How would you implement a Queue?

Queue is a FIFO (First In, First Out) data structure.

class Queue {
    constructor() {
        this.items = [];
    }

    // Add element to back
    enqueue(element) {
        this.items.push(element);
    }

    // Remove and return front element
    dequeue() {
        if (this.isEmpty()) {
            return "Queue is empty";
        }
        return this.items.shift();
    }

    // Return front element without removing
    front() {
        if (this.isEmpty()) {
            return "Queue is empty";
        }
        return this.items[0];
    }

    // Check if queue is empty
    isEmpty() {
        return this.items.length === 0;
    }

    // Return queue size
    size() {
        return this.items.length;
    }

    // Clear queue
    clear() {
        this.items = [];
    }
}

// Usage
const queue = new Queue();
queue.enqueue(1);
queue.enqueue(2);
queue.enqueue(3);
console.log(queue.dequeue()); // 1
console.log(queue.front());   // 2
console.log(queue.size());    // 2

19. How would you implement a LinkedList?

LinkedList is a linear data structure where elements are stored in nodes.

class Node {
    constructor(data) {
        this.data = data;
        this.next = null;
    }
}

class LinkedList {
    constructor() {
        this.head = null;
        this.size = 0;
    }

    // Add element to end
    append(data) {
        const newNode = new Node(data);

        if (!this.head) {
            this.head = newNode;
        } else {
            let current = this.head;
            while (current.next) {
                current = current.next;
            }
            current.next = newNode;
        }
        this.size++;
    }

    // Add element to beginning
    prepend(data) {
        const newNode = new Node(data);
        newNode.next = this.head;
        this.head = newNode;
        this.size++;
    }

    // Remove element by value
    remove(data) {
        if (!this.head) return;

        if (this.head.data === data) {
            this.head = this.head.next;
            this.size--;
            return;
        }

        let current = this.head;
        while (current.next) {
            if (current.next.data === data) {
                current.next = current.next.next;
                this.size--;
                return;
            }
            current = current.next;
        }
    }

    // Print all elements
    print() {
        let current = this.head;
        let result = [];
        while (current) {
            result.push(current.data);
            current = current.next;
        }
        console.log(result.join(" -> "));
    }

    // Get size
    getSize() {
        return this.size;
    }

    // Check if empty
    isEmpty() {
        return this.size === 0;
    }
}

// Usage
const list = new LinkedList();
list.append(1);
list.append(2);
list.append(3);
list.prepend(0);
list.print(); // 0 -> 1 -> 2 -> 3
list.remove(2);
list.print(); // 0 -> 1 -> 3

Additional Resources


Understanding data structures is crucial for writing efficient and maintainable JavaScript code. Practice implementing these structures to master their usage.

Interview angle

  • “Object or Map?” - Map when keys are dynamic, non-string, or insertion order and size matter: any key type, .size in O(1), and direct iteration. Plain objects for fixed, known-shape records. Objects also carry prototype keys, so a bare object as a lookup table can collide with constructor or __proto__.
  • “When do you use WeakMap?” - attaching metadata to objects you do not own, without preventing garbage collection. The entry disappears when the key is collected, which is what makes it right for caches keyed on DOM nodes or component instances.
  • “How do you deduplicate an array?” - [...new Set(arr)] for primitives. It uses SameValueZero, so NaN deduplicates correctly but distinct objects with equal contents do not.
  • “How do you deep-clone?” - structuredClone handles cycles, Maps, Sets, Dates and typed arrays. JSON.parse(JSON.stringify(x)) silently drops functions, undefined and Symbols, and turns Dates into strings.