JavaScript Sets & Maps (ES6+)
Master modern JavaScript collections including Sets, Maps, WeakSets, and WeakMaps for efficient data handling. This is a foundational concept in programming and web interactivity that professional developers rely on daily. The explanations below are written to be beginner-friendly while covering the depth and nuance that comes from real-world JavaScript experience. Take your time with each section and practice the examples
Modern Collection Types
ES6 introduced new collection types that provide better alternatives to traditional objects and arrays for specific use cases.. This is an essential concept that every JavaScript developer must understand thoroughly. In professional development environments, getting this right can mean the difference between code that works reliably and code that breaks in production. The following sections break this down into clear, digestible pieces with practical examples you can try immediately
Set Collection
// Set - Collection of unique values
const uniqueNumbers = new Set([1, 2, 2, 3, 3, 4, 5]);
console.log(uniqueNumbers); // Set {1, 2, 3, 4, 5}
// Set methods
const fruits = new Set();
// Add elements
fruits.add("apple");
fruits.add("banana");
fruits.add("apple"); // Duplicate ignored
console.log(fruits); // Set {"apple", "banana"}
// Check if element exists
console.log(fruits.has("apple")); // true
console.log(fruits.has("orange")); // false
// Remove element
fruits.delete("banana");
console.log(fruits); // Set {"apple"}
// Get size
console.log(fruits.size); // 1
// Clear all elements
fruits.clear();
console.log(fruits.size); // 0
// Set iteration
const colors = new Set(["red", "green", "blue"]);
colors.forEach(color => console.log(color));
for (const color of colors) {
console.log(color);
}
// Set from array
const numbers = [1, 2, 2, 3, 3, 4];
const uniqueNumbers2 = new Set(numbers);
const uniqueArray = [...uniqueNumbers2];
console.log(uniqueArray); // [1, 2, 3, 4]
// Set operations
const set1 = new Set([1, 2, 3]);
const set2 = new Set([2, 3, 4]);
// Union
const union = new Set([...set1, ...set2]);
console.log(union); // Set {1, 2, 3, 4}
// Intersection
const intersection = new Set([...set1].filter(x => set2.has(x)));
console.log(intersection); // Set {2, 3}
// Difference
const difference = new Set([...set1].filter(x => !set2.has(x)));
console.log(difference); // Set {1}Map Collection
// Map - Collection of key-value pairs with any key type
const userMap = new Map();
// Add key-value pairs
userMap.set("name", "John Doe");
userMap.set(1, "User ID 1");
userMap.set({ id: 1 }, "Object key");
userMap.set(true, "Boolean key");
console.log(userMap.get("name")); // "John Doe"
console.log(userMap.get(1)); // "User ID 1"
// Check if key exists
console.log(userMap.has("name")); // true
// Get size
console.log(userMap.size); // 4
// Remove key-value pair
userMap.delete("name");
console.log(userMap.has("name")); // false
// Clear all
userMap.clear();
console.log(userMap.size); // 0
// Map from array of arrays
const userData = [
["name", "John"],
["age", 30],
["city", "NYC"]
];
const userMap2 = new Map(userData);
console.log(userMap2.get("name")); // "John"
// Map iteration
const config = new Map([
["apiUrl", "https://api.example.com"],
["timeout", 5000],
["retries", 3]
]);
// Keys
for (const key of config.keys()) {
console.log(key);
}
// Values
for (const value of config.values()) {
console.log(value);
}
// Entries
for (const [key, value] of config.entries()) {
console.log(`${key}: ${value}`);
}
// forEach
config.forEach((value, key) => {
console.log(`${key} = ${value}`);
});
// Map vs Object
const userObj = {
name: "John",
age: 30
};
const userMap3 = new Map([
["name", "John"],
["age", 30]
]);
// Object keys are always strings
userObj[1] = "one";
console.log(userObj["1"]); // "one"
// Map keys can be any type
userMap3.set(1, "one");
console.log(userMap3.get(1)); // "one"WeakSet & WeakMap
// WeakSet - Weak reference version of Set
const weakSet = new WeakSet();
const obj1 = { name: "John" };
const obj2 = { name: "Jane" };
weakSet.add(obj1);
weakSet.add(obj2);
console.log(weakSet.has(obj1)); // true
console.log(weakSet.has(obj2)); // true
// WeakSet limitations
// - Only objects can be added
// - No iteration methods
// - No size property
// - No clear method
// WeakMap - Weak reference version of Map
const weakMap = new WeakMap();
const user1 = { id: 1 };
const user2 = { id: 2 };
weakMap.set(user1, "John Doe");
weakMap.set(user2, "Jane Smith");
console.log(weakMap.get(user1)); // "John Doe"
// WeakMap limitations
// - Only objects can be keys
// - No iteration methods
// - No size property
// - No clear method
// Practical use case: Caching
const cache = new WeakMap();
function expensiveOperation(obj) {
if (cache.has(obj)) {
return cache.get(obj);
}
const result = {
processed: true,
timestamp: Date.now(),
data: obj
};
cache.set(obj, result);
return result;
}
const data1 = { value: "important data" };
const data2 = { value: "more data" };
console.log(expensiveOperation(data1));
console.log(expensiveOperation(data1)); // Returns cached result
console.log(expensiveOperation(data2));Practice Exercise: Collections
// Exercise: Build a Contact Management System
class ContactManager {
constructor() {
this.contacts = new Map();
this.groups = new Map();
this.tags = new Set();
}
addContact(id, name, email, phone, tags = []) {
const contact = {
id,
name,
email,
phone,
tags: new Set(tags),
createdAt: new Date()
};
this.contacts.set(id, contact);
// Add tags to global tags set
tags.forEach(tag => this.tags.add(tag));
return contact;
}
removeContact(id) {
const contact = this.contacts.get(id);
if (contact) {
this.contacts.delete(id);
return contact;
}
return null;
}
getContact(id) {
return this.contacts.get(id);
}
getAllContacts() {
return Array.from(this.contacts.values());
}
searchContacts(query) {
const results = [];
for (const contact of this.contacts.values()) {
if (contact.name.toLowerCase().includes(query.toLowerCase()) ||
contact.email.toLowerCase().includes(query.toLowerCase())) {
results.push(contact);
}
}
return results;
}
getContactsByTag(tag) {
const results = [];
for (const contact of this.contacts.values()) {
if (contact.tags.has(tag)) {
results.push(contact);
}
}
return results;
}
addGroup(name, contactIds = []) {
const group = new Set(contactIds);
this.groups.set(name, group);
return group;
}
addContactToGroup(contactId, groupName) {
const group = this.groups.get(groupName);
if (group) {
group.add(contactId);
return true;
}
return false;
}
getGroupContacts(groupName) {
const group = this.groups.get(groupName);
if (group) {
return Array.from(group).map(id => this.contacts.get(id)).filter(Boolean);
}
return [];
}
getAllTags() {
return Array.from(this.tags);
}
getStats() {
return {
totalContacts: this.contacts.size,
totalGroups: this.groups.size,
totalTags: this.tags.size
};
}
}
// Test the Contact Manager
const contactManager = new ContactManager();
contactManager.addContact(1, "John Doe", "john@example.com", "555-1234", ["family", "work"]);
contactManager.addContact(2, "Jane Smith", "jane@example.com", "555-5678", ["work"]);
contactManager.addContact(3, "Bob Wilson", "bob@example.com", "555-9012", ["family"]);
console.log("All contacts:", contactManager.getAllContacts().length);
console.log("All tags:", contactManager.getAllTags());
const workContacts = contactManager.getContactsByTag("work");
console.log("Work contacts:", workContacts.length);
contactManager.addGroup("Family", [1, 3]);
const familyGroup = contactManager.getGroupContacts("Family");
console.log("Family group:", familyGroup.length);
console.log("Stats:", contactManager.getStats());