JavaScript Maps

Maps in JavaScript are a mutable collection of key-value pairs, where keys can be of any type. Maps are particularly useful for managing collections of items with unique keys.

Concept and Use Cases

Definition: A JavaScript Map is a collection of key-value pairs, where each key can be of any type (primitive or object). Maps maintain the order of insertion and provide a range of methods to manage and manipulate key-value pairs.

Common Use Cases:

  • Storing and managing collections of items with unique keys.
  • Implementing caches and lookup tables.
  • Efficiently managing dynamic collections with non-string keys.
  • Associating metadata with objects.

When to Use

  • When you need to store key-value pairs and require keys that are not limited to strings.
  • When you need to preserve the insertion order of entries.
  • When performing frequent updates and lookups on a dynamic collection of data.

Time and Space Complexity

Time Complexity:

  • Insertion: O(1)
  • Deletion: O(1)
  • Search: O(1)
  • Iteration: O(n)

Space Complexity:

  • O(n), where n is the number of key-value pairs in the map.

Map Operations and Methods

Creating Maps

Example:

let emptyMap = new Map();
let numberMap = new Map([[1, 'one'], [2, 'two'], [3, 'three']]);
let mixedMap = new Map([
    ['stringKey', 'value1'],
    [42, 'value2'],
    [{key: 'object'}, 'value3']
]);

Adding and Removing Entries

Example:

let map = new Map();
map.set('name', 'John');
map.set('age', 30);
map.set('age', 31);  // Updates the value for 'age'
console.log(map);  // Output: Map { 'name' => 'John', 'age' => 31 }

map.delete('name');
console.log(map);  // Output: Map { 'age' => 31 }

Checking for Existence

Example:

let map = new Map([['name', 'John'], ['age', 30]]);
console.log(map.has('name'));  // Output: true
console.log(map.has('address'));  // Output: false

Getting the Size of a Map

Example:

let map = new Map([['name', 'John'], ['age', 30]]);
console.log(map.size);  // Output: 2

Iterating Over Maps

Example:

let map = new Map([['name', 'John'], ['age', 30]]);

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

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

Clearing a Map

Example:

let map = new Map([['name', 'John'], ['age', 30]]);
map.clear();
console.log(map.size);  // Output: 0

Converting Maps to Arrays

Example:

let map = new Map([['name', 'John'], ['age', 30]]);
let keysArray = Array.from(map.keys());
let valuesArray = Array.from(map.values());
let entriesArray = Array.from(map.entries());

console.log(keysArray);  // Output: ['name', 'age']
console.log(valuesArray);  // Output: ['John', 30]
console.log(entriesArray);  // Output: [['name', 'John'], ['age', 30]]

Practical Tips and Tricks

  • Using Maps for Object Metadata: Maps can be used to associate metadata with objects without modifying the objects.

    Example:

    let obj1 = {id: 1};
    let obj2 = {id: 2};
    
    let metadataMap = new Map();
    metadataMap.set(obj1, {role: 'admin'});
    metadataMap.set(obj2, {role: 'user'});
    
    console.log(metadataMap.get(obj1));  // Output: { role: 'admin' }
    
  • Using Complex Keys: Maps allow for keys that are not limited to strings, such as objects or functions.

    Example:

    let map = new Map();
    let key1 = {};
    let key2 = () => {};
    
    map.set(key1, 'value1');
    map.set(key2, 'value2');
    
    console.log(map.get(key1));  // Output: 'value1'
    console.log(map.get(key2));  // Output: 'value2'
    
  • Efficient Lookup Table: Maps provide efficient O(1) lookups, making them suitable for implementing lookup tables.

    Example:

    let lookup = new Map([['a', 1], ['b', 2], ['c', 3]]);
    console.log(lookup.get('b'));  // Output: 2
    

Common Gotchas

  • Using Objects as Keys: When using objects as keys, the same object reference must be used for lookups.

    Example:

    let map = new Map();
    let obj = {id: 1};
    
    map.set(obj, 'value');
    console.log(map.get({id: 1}));  // Output: undefined (different object reference)
    console.log(map.get(obj));  // Output: 'value'
    
  • Order of Insertion: Maps maintain the order of insertion, which can affect iteration and operations.

    Example:

    let map = new Map([['a', 1], ['b', 2], ['c', 3]]);
    console.log(Array.from(map.keys()));  // Output: ['a', 'b', 'c']
    
  • Not Using clear Correctly: The clear method removes all entries from a map but doesn't return the map itself.

    Example:

    let map = new Map([['a', 1], ['b', 2]]);
    map.clear();
    console.log(map.size);  // Output: 0
    

Advanced Topics

WeakMaps

WeakMaps are similar to maps but only contain object keys and do not prevent garbage collection of keys.

Example:

let weakMap = new WeakMap();
let obj = {};

weakMap.set(obj, 'value');
console.log(weakMap.get(obj));  // Output: 'value'

obj = null;  // The entry in the WeakMap will be garbage collected

Map Operations Using Utility Functions

Example:

function mergeMaps(map1, map2) {
    return new Map([...map1, ...map2]);
}

function mapKeysToArray(map) {
    return Array.from(map.keys());
}

function mapValuesToArray(map) {
    return Array.from(map.values());
}

let map1 = new Map([['a', 1], ['b', 2]]);
let map2 = new Map([['c', 3], ['d', 4]]);

let mergedMap = mergeMaps(map1, map2);
console.log(mergedMap);  // Output: Map { 'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4 }

console.log(mapKeysToArray(mergedMap));  // Output: ['a', 'b', 'c', 'd']
console.log(mapValuesToArray(mergedMap));  // Output: [1, 2, 3, 4]

Map Algorithms

Below are some common map algorithms you should be familiar with:

Counting Frequencies

Counting frequencies involves determining how often each element appears in an array.

Example:

function countFrequencies(arr) {
    let frequencyMap = new Map();
    for (let item of arr) {
        if (frequencyMap.has(item)) {
            frequencyMap.set(item, frequencyMap.get(item) + 1);
        } else {
            frequencyMap.set(item, 1);
        }
    }
    return frequencyMap;
}

let array = ['a', 'b', 'a', 'c', 'b', 'a'];
console.log(countFrequencies(array));  // Output: Map { 'a' => 3, 'b' => 2, 'c' => 1 }

Finding Unique Keys

Finding unique keys identifies elements that appear exactly once in an array.

Example:

function findUniqueKeys(arr) {
    let map = new Map();
    for (let item of arr) {
        map.set(item, (map.get(item) || 0) + 1);
    }
    let uniqueKeys = [];
    for (let [key, value] of map) {
        if (value === 1) {
            uniqueKeys.push(key);
        }
    }
    return uniqueKeys;
}

let array = ['a', 'b', 'a', 'c', 'b', 'd'];
console.log(findUniqueKeys(array));  // Output: ['c', 'd']

Merging Multiple Maps

Merging multiple maps combines the key-value pairs of multiple maps into one.

Example:

function mergeMultipleMaps(...maps) {
    return maps.reduce((merged, map) => {
        for (let [key, value] of map) {
            merged.set(key, value);
        }
        return merged;
    }, new Map());
}

let map1 = new Map([['a', 1], ['b', 2]]);
let map2 = new Map([['c', 3], ['d', 4]]);
let map3 = new Map([['e', 5], ['f', 6]]);

let mergedMap = mergeMultipleMaps(map1, map2, map3);
console.log(mergedMap);  // Output: Map { 'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5, 'f' => 6 }

Interview Tips and Tricks

  • Understand Map Methods: Be familiar with methods like set, get, has, delete, and clear.

  • Practice Map Use Cases: Work on problems involving counting frequencies, managing caches, and performing efficient lookups.

  • Handle Edge Cases: Consider scenarios with empty maps, maps with non-string keys, and maps with nested structures.

  • Use WeakMaps for Efficient Memory Management: Utilize WeakMaps when you need keys to be garbage collected.

Common Mistakes

  • Misusing Object Keys: Remember that objects used as keys must be the same reference for lookups to work.

  • Forgetting Order of Insertion: Maps maintain the order of insertion, which may affect iteration and operations.

  • Not Using clear Correctly: The clear method removes all entries but does not return the map itself.

By mastering JavaScript maps and understanding their intricacies, you will be well-equipped to handle a variety of interview questions and real-world problems involving key-value pairs. Regular practice and a solid grasp of advanced topics will deepen your understanding and improve your problem-solving skills.

Practice Problems

Word PatternDifficulty: Easy

Given two patterns and their corresponding strings, determine if the strings follow the same pattern by comparing each string with all possible patterns.

Loading...

Given two lists of integers, find the pair of numbers with the smallest sum of their indices in the lists when sorted separately.

Loading...

Subdomain Visit CountDifficulty: Medium

Count the number of visits for each subdomain given an array of domain names and their corresponding visit counts.

Loading...

Let's continue exploring the next page. Take your time, and proceed when you're ready.

Lesson completed?

Found a bug, typo, or have feedback?

Let me know