How to Use Set and Map in JavaScript: An In-Depth Guide

JavaScript code on a screen

JavaScript developers have access to several capable built-in data structures for storing and organizing information. Arrays and plain Objects cover a wide variety of use cases. However, the Set and Map objects provide additional unique functionality for handling data collections.

Sets excel at ensuring value uniqueness within groups and removing duplicates.
Maps improve over plain Objects by supporting any data type as keys while delivering solid performance.

Knowing when to utilize a Set or Map can significantly enhance what you build. This guide explores Sets and Maps in JavaScript in-depth from an advanced full-stack perspective.

You will learn:

  • Real-world use cases where Sets and Maps shine over Arrays and Objects
  • Guidelines for choosing the right data structure for your data
  • Performance benchmarks and time complexity comparisons
  • Initialization best practices and data manipulation methods
  • In-depth look at Sets vs Arrays and Maps vs Objects
  • Compatibility and adoption info for older browsers
  • Examples and statistics illustrating appropriate usage

So whether you are a JavaScript expert looking to round out your data structure skills or building full-stack data-driven web apps, exploring Sets and Maps in depth will help take your code to the next level.

Common Use Cases for JavaScript Sets

While JavaScript Arrays certainly have broad utility, Sets open additional possibilities in key areas:

Removing Duplicate Values

Ensuring uniqueness within groups of values is a common need. With Array variables, you might have:

const fruits = [‘apple‘,‘banana‘,‘apple‘,‘orange‘];

The duplicate "apple" may cause issues down the line. To guarantee value uniqueness:

const uniqueFruits = new Set(fruits); // No duplicates

Much simpler than manually weeding them out!

Union, Intersection, and Difference Operations

Sets also enable set theory operations for things like:

  • Finding overlap between data sets
  • Combining multiple groups in different ways

For example, given two sets:

const setA = new Set([1, 2, 3]); 
const setB = new Set([2, 3, 4]);

You can quickly intersect them to find values common to both:

const intersection = new Set([...setA].filter(x => setB.has(x))); // {2, 3}

Other possible combinations are unions (combine all values) or differences (values in first not second).

This is extremely useful functionality in practice when handling Venn diagrams of data sets.

Cached Lookups

Building unique sets allows leveraging faster O(1) lookup times in many cases vs O(n) with indexOf scans on arrays:

function contains(set, value) {
  return set.has(value); // O(1) ops   
}

function contains(array,value) {
  return array.indexOf(value) > -1; // O(n) ops
} 

So Sets can drastically simplify certain checks.

These are just a few examples where Sets provide elegant solutions and superior performance over array equivalents.

When to Use JavaScript Maps over Objects

While plain Objects are ubiquitous across JavaScript, Maps open additional possibilities:

Multi-Type Keys

One major constraint of plain Objects is keys must be either strings or symbols. This is not the case for Maps, where any value can be used as a key:

const userIDMap = new Map();

userIDMap.set(Date.now(), Math.random()); // Date & number as keys 

The flexibility opens entirely new techniques for organizing data.

Key Iteration

It‘s simple to iterate the keys, values or key-value pairs stored in a JavaScript Map thanks to methods like:

  • map.keys()
  • map.values()
  • map.entries()

Combined with for..of loops, this provides concise ways to access contents that plain Objects can‘t match without Object.keys(), Object.values(), Object.entries() workarounds.

Higher Performance

JavaScript Maps also better optimize frequent insertions and removals of key-value pairs compared to plain Objects.

The difference may be negligible in smaller data sets but becomes pronounced at scale with 1000s or more entries.

So when building anything from caches to metadata stores, Maps will tend to outperform Objects storing equivalent data.

Between these core advantages, Maps become the obvious choice for certain data storage scenarios as you‘ll see through examples next.

Real-World Examples Using JavaScript Maps

Here are some concrete examples of applying JavaScript Maps for common programming objectives:

User Session Storage

Tracking logged in users and metadata using a Map:

const userSessions = new Map();

userSessions.set(‘userName123‘, {
  lastActive: Date.now(), 
  ip: ‘127.0.0.1‘
});

userSessions.get(‘userName123‘).lastActive; // Access last active 

Better than using plain objects with username strings as keys.

Data Validation

Validating forms and data types, using anything as keys:

const types = new Map();

types.set(String, val => typeof val === ‘string‘);
types.set(Number, val => typeof val === ‘number‘);
types.set(Boolean, val => typeof val === ‘boolean‘);

function validate(key, val) {
  return types.get(key)(val); // Call validator fn
} 

validate(String, ‘x‘); // true
validate(Number, ‘x‘); // false

Metadata Storage

Adding metadata to objects while separating concerns:

class User {
  // Omit fields  
}

const userNotes = new Map(); 

userNotes.set(user1, ‘Inactive‘); // Add notes by user 

userNotes.get(user1); // Check notes

There are many other examples like implementing LRU caches, aggregates based on groups (using anything as a key), and custom collectible registries in games.

The common theme is using keys flexibly leads to simpler and better code organization.

Set and Map Performance Characteristics

So clearly Sets and Maps enable certain techniques that array and object equivalents can‘t match. But how do they actually perform under the hood?

The core performance advantage of Sets and Maps comes from leveraging hash tables (key-value stores) as implemented natively in JavaScript engines.

This contrasts arrays and objects which map sequentially to memory locations and require dynamic resizing at scale.

The result is faster lookups, inserts and removals for Sets and Maps across most operations:

Sets and maps faster operations table

To highlight the speed advantage, here is a benchmark inserting 1000 elements:

Set insertion benchmark vs Array

As you can see, the Set significantly outperforms equivalent array insertions as the number of elements scales.

Now keep in mind there are still use cases better served by arrays (indexing, splicing, etc) or plain objects (smaller data with string/symbol keys).

But for raw data storage and manipulation, Sets and Maps have superior performance by design.

Understanding these inherent performance advantages allows properly applying the right tool for particular data challenges.

Initializing JavaScript Sets and Maps

Now that you know where Sets and Maps provide value, how do you actually start using them?

First, Sets support initialization in a couple helpful ways:

No Args for Empty Set

const ids = new Set(); // empty  

Values as Args

const roles = new Set([‘admin‘,‘manager‘]); 

Arrays/Sets as Args

const newSet = new Set([...oldSet]); // Copy set
const noDupeArr = new Set(arrWithDupes); // De-dupe array 

Some options for Maps:

No Args for Empty Map

const prices = new Map();

2D Key-Value Pairs

const fruitsCalories = new Map([ 
  [‘apple‘, 95],
  [‘orange‘, 45]
]);

Map of Maps

const data = new Map([
  [‘key1‘, map1],
  [‘key2‘, map2]     
]);

Set as Keys

const cache = new Map();
cache.set(new Set([a,b]), v); 

So any data types can be passed in Flexibly on initialization.

Now for data manipulation…

Modifying and Accessing Sets and Maps

Core methods allow interacting with data in Sets and Maps:

Sets

  • add()/delete() for writing
  • has() checks membership
  • size gets length
  • clear() empties Set
  • keys(), values(), entries()

Maps

  • set()/delete() to write
  • has() checks keys
  • get() accesses values
  • size gets size
  • clear() clears
  • keys(), values(), entries()

You iterate using for..of loops:

// Set iterations
for (let item of set.values()) {}  

// Map iterations 
for (let [key, val] of map.entries()) {}   

And standard Array methods like map() or forEach() work as well:

map.forEach((v, k) => {}); 

[...set].map(item => {});  

So modifying and reading Sets and Maps stays simple and intuitive despite the power under the hood.

Sets vs Arrays: When to Use Each

Now you may be wondering…when should you actually prefer Sets vs simply using arrays in JavaScript?

Here is a breakdown of factors to consider:

Use a Set when you need:

  • Unique values (removing duplicates)
  • Faster lookups than Array.prototype.indexOf()
  • Set operations like unions and intersections
  • Storage without indices or ordering

Use an Array when you require:

  • Indexed or ordered access
  • Additional Array methods like .map(), .filter() etc
  • Integrating with other array-based code
  • Sorting and comparing values

Other Considerations

  • Compatibility: Sets require ES6/ES2015 support
  • Performance: Sets faster for lookups, inserts, deletes
  • Syntax: Array syntax/coding patterns more common

So if you need uniqueness, Set operations, or hashtable performance, choose Set. Otherwise arrays may be simpler.

Maps vs Plain Objects: Key Differences

Similarly, opting for a Map over plain Object comes with tradeoffs:

Use Maps when you need:

  • Keys other than strings or symbols
  • Faster large scale inserts and deletes
  • Iteration methods like .entries() and .values()
  • Better performance for frequent changes

Use Plain Objects when:

  • Keys must be strings or symbols
  • Smaller data sizes
  • Prototypal inheritance is useful
  • Less dynamic size changes

Additional Considerations

  • Compatibility: Maps require ES6/ES2015+
  • Performance: Maps faster for freq. inserts/deletes
  • Syntax: Objects have more familiar syntax

So opt for Maps when leveraging non-string keys or requiring scaling performance. Else plain Objects may be preferable for familiarity and smaller data sizes.

Browser Compatibility and Support

As ES6/ES2015 features, Sets and Maps are not supported in legacy JavaScript environments like old IE without polyfills or transpilation.

However, most modern browsers have implemented native support with >95% global usage as of 01/2023:

Set and Map Browser Support Tables

So you can utilize Sets and Maps in most situations. But ensure fallback behavior if supporting older browsers.

Transpilers like Babel allow compiling ES6+ features to ES5 equivalents. Polyfill shims also exist for Sets and Maps.

For server-side/backend JavaScript, platforms like Node.js have included native Set/Map implementations since v6+ LTS versions (Oct 2016).

So concerns around compatibility continue fading over time.

Tips for Effective Usage

Here are some best practices for clean and effective usage of Sets and Maps in your code:

  • Validate values on insertion rather than after to avoid issues. For example with Maps:

    function setValue(key, val) {
      if (!validate(val)) {
        return; 
      }
    
      data.set(key, val); // inserts if valid 
    }
  • Leverage parameterized constructors to initialize populated Set/Maps cleanly:

    function UserSet(users = []) {
      return new Set(users); 
    }
    
    const admins = new UserSet([/* admin users */]);
  • Clear Sets or Maps fully before re-use to avoid data bleeding:

    function fillMap(map) {
      map.clear();
      // set data
    }  
  • Use object references as keys for retrieval later:

    const cache = new Map(); 
    
    cache.set(obj, ‘Cached data‘); 
    
    cache.get(obj); // Retrieve via original reference  

These tips help avoid common pitfalls when working with Sets and Maps in JavaScript.

Conclusion

Set and Map were once niche features in JavaScript but now enjoy widespread support. They enable:

  • Guaranteed unique values with Set
  • Flexible key types beyond strings with Map

These open entire categories of solutions previously tricky or inefficient in native JavaScript.

Knowing how to properly apply Sets & Maps leads to cleaner code and better runtime performance. Sorting unique data, complex lookups, key-value metadata are all great candidates.

Plain arrays and objects still dominate JavaScript. But augmenting your data structure skills with Sets and Maps provides extremely valuable additions to your programming toolkit.

I encourage you to try integrating Sets and Maps into your projects where applicable to reap the benefits!

Similar Posts