πŸš€Debugging Microservices & Distributed Systems
7 min read
intermediate

JavaScript Sets and Maps: Beyond Arrays and Objects

How to handle unique values and key-value pairs properly without type coercion and performance issues


Before ES6 introduced Sets and Maps, we had limited options for storing collections of data in JavaScript. We used objects for key-value pairs and arrays for lists. This led to common problems:

JavaScript
// Problem 1: Using arrays for unique values
const userIds = [1, 2, 2, 3]
const uniqueIds = userIds.filter((id, index) =>
userIds.indexOf(id) === index
)
console.log(uniqueIds)
// [1, 2, 3] βœ… Correct, but inefficient!
// Has to loop through array multiple times
// Problem 2: Numbers and strings collide as keys
const cache = {}
cache[123] = "data" // Using number as key
cache["123"] = "other data" // Using string as key
console.log(cache[123]) ❌ // "other data" - both keys got converted to string "123"
// Problem 3: Objects as keys don't work
const userMap = {}
const user = {id: 1}
userMap[user] = "data"
console.log(userMap[user]) // "data"
console.log(userMap["[object Object]"]) ❌ // "data" - same value!
// Both objects become "[object Object]"
const user2 = {id: 2}
userMap[user2] = "different data"
console.log(userMap[user]) ❌ // "different data" - first value got overwritten!

What are Sets and Maps?

Sets and Maps are specialized data structures in JavaScript, each designed to solve specific problems that arrays and objects handle poorly.

A Set is a collection of unique values. Think of it like a bag that automatically removes duplicates.

When you add the same value twice, the Set keeps only one copy. It’s perfect for maintaining lists where each item should appear only once.

JavaScript
// Set automatically handles uniqueness
const uniqueNumbers = new Set([1, 1, 2, 2, 3])
console.log(uniqueNumbers) // βœ… Set(3) {1, 2, 3}
// Fast lookups for existence checks
uniqueNumbers.has(1) // true
uniqueNumbers.has(4) // false
// Easy to add and remove items
uniqueNumbers.add(4) // Set(4) {1, 2, 3, 4}
uniqueNumbers.delete(1) // Set(3) {2, 3, 4}

A Map is a collection of key-value pairs where keys can be any type - numbers, strings, objects, even functions. Unlike objects, which convert all keys to strings, Maps preserve the type of the key. This makes them ideal for creating dictionaries or caches where the key type matters:

JavaScript
// Map maintains key types
const userProfiles = new Map()
// Numbers stay numbers
userProfiles.set(123, "John")
userProfiles.set("123", "Jane") // Different from 123
// Objects can be keys
const user = {id: 1}
userProfiles.set(user, "User data")
console.log(userProfiles.get(123)) // "John"
console.log(userProfiles.get("123")) // "Jane"
console.log(userProfiles.get(user)) // "User data"

Here’s how Sets and Maps solve the two problems we saw earlier with arrays and objects.

JavaScript
// Sets for unique values
const userIds = new Set([1, 2, 2, 3]) // Automatically unique
console.log(userIds) // Set(3) {1, 2, 3}
// Maps for proper key-value storage
const cache = new Map()
cache.set(123, "data") // Number key
cache.set("123", "other") // String key - different!
const userMap = new Map()
const user = {id: 1}
userMap.set(user, "data") // Object as key - works!

Sets and Maps excel at handling data relationships, caching, and uniqueness checks. Each has specific use cases where they outperform traditional arrays and objects.

When to Use Sets

Sets shine when you need fast lookups and uniqueness guarantees in your data. In a tag system, you can instantly check if an article has a specific tag without looping through an array.

JavaScript
// Fast existence checks
const adminPermissions = new Set(['edit', 'delete', 'create'])
if (adminPermissions.has('delete')) { // Instant lookup
deleteResource()
}
// Tracking unique values
const uniqueVisitors = new Set()
uniqueVisitors.add(userId) // Duplicates handled automatically

When to Use Maps

Maps are perfect when you need to associate data with any type of key - like caching API responses by URL, storing user preferences, or maintaining a relationship between DOM elements and their data.

JavaScript
// Type-safe keys
const apiCache = new Map()
apiCache.set('/api/users', userData) // URL as exact string
apiCache.set(404, errorData) // Number stays number
// Objects as keys
const elementStates = new Map()
const button = document.querySelector('#submit')
elementStates.set(button, { // DOM element as key
clicks: 0,
lastClicked: null
})

Performance Trade-offs

Sets and Maps provide O(1) operations through their hash table implementation, compared to arrays which have O(n) for lookups and objects which coerce keys to strings.

However, this speed comes at a cost: Sets and Maps use more memory than arrays and objects. The hash table structure that enables their fast operations requires additional memory overhead to maintain.

For Sets specifically, the hash table ensures that duplicate detection is instantaneous, rather than requiring a full array scan. Maps achieve their performance by using a similar structure but storing both the key and value in the hash table.

JavaScript
// Set Performance
const set = new Set()
set.add(value) // O(1) - constant time hash computation
set.has(value) // O(1) - direct hash table lookup
set.delete(value) // O(1) - immediate hash table removal
set.size // O(1) - size counter is maintained
// Map Performance
const map = new Map()
map.set(key, value) // O(1) - hash table insertion
map.get(key) // O(1) - direct hash lookup
map.has(key) // O(1) - hash existence check
map.delete(key) // O(1) - hash table deletion
map.size // O(1) - continuous size tracking

Sets and Maps excel at handling user sessions. Sets provide instant O(1) lookups to check if a user is logged in - much faster than searching through an array of user IDs. Maps let us store session data with any type of user identifier (number, string, or object) without key type conversion issues.

JavaScript
class SessionManager {
constructor() {
this.activeSessions = new Map() // userId -> session data
this.loggedInUsers = new Set() // unique user IDs
}
login(userId, sessionData) {
this.loggedInUsers.add(userId) // O(1) add
this.activeSessions.set(userId, {
data: sessionData,
startTime: Date.now()
})
}
isLoggedIn(userId) {
return this.loggedInUsers.has(userId) // O(1) check
}
getSessionData(userId) {
return this.activeSessions.get(userId) // O(1) lookup
}
}

The SessionManager example shows how Sets can track unique active users while Maps store detailed session data. This combination is particularly powerful because it separates concerns - the Set handles uniqueness while the Map handles data association.

The instant lookup times make this ideal for high-traffic applications where performance is critical.

When Not to Use Sets and Maps

Sets and Maps come with memory overhead from their hash table structure. For small collections or simple string keys, arrays and objects might be more efficient.

JavaScript
// 1. You need array methods
const numbers = [1, 2, 3, 4]
numbers.map(n => n * 2) // Can't do this with Sets
numbers.filter(n => n > 2) // No built-in Set filtering
numbers.reduce((a, b) => a + b) // No Set reduction
// 2. You need index access
const items = ['first', 'second', 'third']
console.log(items[0]) // Direct index access
console.log(items.indexOf('second')) // Position matters
// 3. You need JSON serialization
const user = {name: 'John', age: 30}
JSON.stringify(user) // Works fine
JSON.stringify(new Map()) // Empty object {}
JSON.stringify(new Set()) // Empty array []
// 4. You need simple string-only keys
// Using Map here would be overkill
const config = {
apiKey: '123',
baseUrl: 'api.example.com'
}

Sets and Maps aren’t just alternatives to arrays and objects - they’re specialized tools that make specific programming patterns more efficient and reliable. Use them when their unique characteristics align with your needs, and you’ll write more robust and performant code.


Related Articles

If you enjoyed this article, you might find these related pieces interesting as well.

Recommended Engineering Resources

Here are engineering resources I've personally vetted and use. They focus on skills you'll actually need to build and scale real projects - the kind of experience that gets you hired or promoted.

Imagine where you would be in two years if you actually took the time to learn every day. A little effort consistently adds up, shaping your skills, opening doors, and building the career you envision. Start now, and future you will thank you.


This article was originally published on https://www.trevorlasn.com/blog/sets-and-maps-in-javascript. It was written by a human and polished using grammar tools for clarity.

Interested in a partnership? Shoot me an email at hi [at] trevorlasn.com with all relevant information.