JavaScript Map and Set Collections
JavaScript's ES6 (ECMAScript 2015) introduced two important collection types that have become essential tools for modern JavaScript development: Map
and Set
. These collection types provide solutions for common programming scenarios that were previously difficult to handle with just arrays and objects.
Introduction to Maps and Sets
Before ES6, JavaScript developers primarily used objects ({}
) for key-value storage and arrays ([]
) for lists. While these structures work well for many cases, they have limitations that Map
and Set
address.
Let's explore these powerful collection types and see how they can enhance your JavaScript code.
Map Collection
What is a Map?
A Map
is a collection of key-value pairs where both the keys and values can be of any data type. Unlike regular objects, maps remember the original insertion order of the keys.
Creating a Map
You can create a new Map and add elements in several ways:
// Creating an empty Map
const userRoles = new Map();
// Adding key-value pairs
userRoles.set('john', 'admin');
userRoles.set('jane', 'editor');
userRoles.set('bob', 'subscriber');
console.log(userRoles);
// Output: Map(3) { 'john' => 'admin', 'jane' => 'editor', 'bob' => 'subscriber' }
You can also initialize a Map with an array of key-value pairs:
const userRoles = new Map([
['john', 'admin'],
['jane', 'editor'],
['bob', 'subscriber']
]);
console.log(userRoles);
// Output: Map(3) { 'john' => 'admin', 'jane' => 'editor', 'bob' => 'subscriber' }
Key Map Methods and Properties
Let's explore the essential Map methods:
Getting and Setting Values
const userPreferences = new Map();
// Set values
userPreferences.set('theme', 'dark');
userPreferences.set('fontSize', 16);
// Get values
console.log(userPreferences.get('theme')); // Output: dark
console.log(userPreferences.get('fontSize')); // Output: 16
console.log(userPreferences.get('language')); // Output: undefined (key doesn't exist)
Checking if a Key Exists
console.log(userPreferences.has('theme')); // Output: true
console.log(userPreferences.has('language')); // Output: false
Deleting Entries
userPreferences.delete('fontSize');
console.log(userPreferences.has('fontSize')); // Output: false
console.log(userPreferences); // Output: Map(1) { 'theme' => 'dark' }
Size Property
console.log(userPreferences.size); // Output: 1
Clearing a Map
userPreferences.clear();
console.log(userPreferences.size); // Output: 0
Iterating Through a Map
Maps are iterable, and there are several ways to loop through them:
const productPrices = new Map([
['apple', 1.2],
['banana', 0.9],
['orange', 1.5]
]);
// Iterate over key-value pairs
for (const [product, price] of productPrices) {
console.log(`${product}: $${price}`);
}
// Output:
// apple: $1.2
// banana: $0.9
// orange: $1.5
// Using forEach
productPrices.forEach((price, product) => {
console.log(`${product}: $${price}`);
});
// Output:
// apple: $1.2
// banana: $0.9
// orange: $1.5
Map-Specific Iteration Methods
Maps provide specific methods for getting iterators:
// Get an iterator for keys
const keyIterator = productPrices.keys();
for (const key of keyIterator) {
console.log(key); // Output: apple, banana, orange
}
// Get an iterator for values
const valueIterator = productPrices.values();
for (const value of valueIterator) {
console.log(value); // Output: 1.2, 0.9, 1.5
}
// Get an iterator for key-value pairs
const entryIterator = productPrices.entries();
for (const entry of entryIterator) {
console.log(entry); // Output: ['apple', 1.2], ['banana', 0.9], ['orange', 1.5]
}
Maps vs Objects
Maps offer several advantages over regular objects:
- Keys of any type: Map keys can be any value (including functions, objects, and primitives), while object keys must be strings or symbols.
- Size tracking: Maps track their size with the
size
property; objects require manual counting. - Iteration order: Maps preserve insertion order; regular objects don't guarantee order.
- Performance: Maps perform better when keys are frequently added and removed.
// Using non-string keys in a Map
const userMap = new Map();
const userObject = { name: 'John' };
const userFunction = () => console.log('Hello');
userMap.set(userObject, 'User data');
userMap.set(userFunction, 'Function data');
userMap.set(42, 'Number data');
console.log(userMap.get(userObject)); // Output: User data
console.log(userMap.get(userFunction)); // Output: Function data
console.log(userMap.get(42)); // Output: Number data
Set Collection
What is a Set?
A Set
is a collection of unique values of any type. Each value can only appear once in a Set.
Creating a Set
// Creating an empty Set
const uniqueVisitors = new Set();
// Adding values
uniqueVisitors.add('user1');
uniqueVisitors.add('user2');
uniqueVisitors.add('user1'); // Duplicate, will be ignored
console.log(uniqueVisitors);
// Output: Set(2) { 'user1', 'user2' }
You can also initialize a Set with an array:
const uniqueTags = new Set(['javascript', 'programming', 'web', 'javascript']);
console.log(uniqueTags);
// Output: Set(3) { 'javascript', 'programming', 'web' }
Key Set Methods and Properties
Adding and Checking Elements
const favoriteColors = new Set();
// Add values
favoriteColors.add('blue');
favoriteColors.add('red');
favoriteColors.add('green');
// Check if a value exists
console.log(favoriteColors.has('blue')); // Output: true
console.log(favoriteColors.has('yellow')); // Output: false
Deleting Elements
favoriteColors.delete('red');
console.log(favoriteColors); // Output: Set(2) { 'blue', 'green' }
Size Property
console.log(favoriteColors.size); // Output: 2
Clearing a Set
favoriteColors.clear();
console.log(favoriteColors.size); // Output: 0
Iterating Through a Set
Sets are iterable, so you can use methods like forEach
or a for...of
loop:
const programmingLanguages = new Set(['JavaScript', 'Python', 'Java', 'C++']);
// Using forEach
programmingLanguages.forEach(lang => {
console.log(`I know ${lang}`);
});
// Output:
// I know JavaScript
// I know Python
// I know Java
// I know C++
// Using for...of
for (const lang of programmingLanguages) {
console.log(`I know ${lang}`);
}
Set Operations
Sets are great for performing mathematical set operations like union, intersection, and difference:
const setA = new Set([1, 2, 3, 4]);
const setB = new Set([3, 4, 5, 6]);
// Union: combine all elements from both sets
const union = new Set([...setA, ...setB]);
console.log(union);
// Output: Set(6) { 1, 2, 3, 4, 5, 6 }
// Intersection: elements that exist in both sets
const intersection = new Set([...setA].filter(x => setB.has(x)));
console.log(intersection);
// Output: Set(2) { 3, 4 }
// Difference: elements in setA that are not in setB
const difference = new Set([...setA].filter(x => !setB.has(x)));
console.log(difference);
// Output: Set(2) { 1, 2 }
Practical Applications
Using Maps for Complex Data Management
Maps are excellent for scenarios where you need to associate data with specific keys:
// User session management
const userSessions = new Map();
function createSession(userId, data) {
userSessions.set(userId, {
loggedInAt: new Date(),
data: data,
isActive: true
});
}
function getSessionInfo(userId) {
return userSessions.get(userId) || { error: 'No active session' };
}
createSession('user123', { username: 'johndoe', permissions: ['read', 'write'] });
console.log(getSessionInfo('user123'));
// Output: { loggedInAt: Date object, data: { username: 'johndoe', permissions: ['read', 'write'] }, isActive: true }
Using Sets for Filtering Unique Values
Sets are perfect for removing duplicates from arrays:
// Remove duplicates from an array
const messyArray = [1, 2, 3, 1, 4, 2, 5, 1, 4];
const uniqueValues = [...new Set(messyArray)];
console.log(uniqueValues); // Output: [1, 2, 3, 4, 5]
Real-world Example: Cache Implementation with Map
class SimpleCache {
constructor(maxSize = 100) {
this.cache = new Map();
this.maxSize = maxSize;
}
set(key, value, timeToLiveMs = 5000) {
// Clear old entries if we reached max size
if (this.cache.size >= this.maxSize) {
const oldestKey = this.cache.keys().next().value;
this.cache.delete(oldestKey);
}
// Set expiration time
const expiresAt = Date.now() + timeToLiveMs;
this.cache.set(key, {
value,
expiresAt
});
// Set timeout to automatically delete the item when it expires
setTimeout(() => {
if (this.cache.has(key) && this.cache.get(key).expiresAt <= Date.now()) {
this.cache.delete(key);
}
}, timeToLiveMs);
}
get(key) {
if (!this.cache.has(key)) {
return undefined;
}
const item = this.cache.get(key);
// Check if the item has expired
if (item.expiresAt <= Date.now()) {
this.cache.delete(key);
return undefined;
}
return item.value;
}
}
// Usage
const cache = new SimpleCache(2);
cache.set('user1', { name: 'John', age: 30 });
console.log(cache.get('user1')); // Output: { name: 'John', age: 30 }
Real-world Example: Tracking Unique Site Visitors with Set
class VisitorTracker {
constructor() {
this.dailyVisitors = new Set();
this.weeklyVisitors = new Set();
this.monthlyVisitors = new Set();
// Reset daily visitors at midnight
setInterval(() => {
this.dailyVisitors.clear();
}, 86400000); // 24 hours
}
recordVisit(visitorId) {
this.dailyVisitors.add(visitorId);
this.weeklyVisitors.add(visitorId);
this.monthlyVisitors.add(visitorId);
}
getDailyStats() {
return {
uniqueVisitors: this.dailyVisitors.size,
visitorList: [...this.dailyVisitors]
};
}
getWeeklyStats() {
return {
uniqueVisitors: this.weeklyVisitors.size
};
}
}
// Usage
const tracker = new VisitorTracker();
tracker.recordVisit('user123');
tracker.recordVisit('user456');
tracker.recordVisit('user123'); // Duplicate, will be counted only once
console.log(tracker.getDailyStats().uniqueVisitors); // Output: 2
Summary
Maps and Sets are powerful collection types in modern JavaScript that significantly enhance how we can structure and manipulate data:
Map Collection:
- A key-value collection where keys can be any data type
- Maintains insertion order of elements
- Provides direct methods for adding, retrieving, and deleting elements
- Better performance for frequent additions and deletions than objects
Set Collection:
- A collection of unique values
- Automatically eliminates duplicates
- Perfect for operations like unions and intersections
- Great for testing element membership
Understanding when and how to use these collection types is an essential skill for modern JavaScript development. They provide elegant solutions to common programming problems and can make your code more readable and efficient.
Additional Resources
To deepen your understanding of JavaScript Maps and Sets:
Exercises
Test your understanding with these exercises:
- Create a function that counts the frequency of each word in a string using a Map.
- Write a function that takes two arrays and returns an array containing only the elements that appear in both arrays (using Sets).
- Implement a simple shopping cart using a Map where product IDs are keys and quantities are values.
- Create a function that removes all duplicates from an array of objects based on a specific property (e.g., remove duplicate users based on their email).
- Build a simple tag system that ensures tags are unique and case-insensitive using a Set.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)