Skip to main content

[Medium] Set & Map

1. What are Set and Map?

What are Set and Map?

Set and Map are two data structures introduced in ES6. They solve specific use cases better than plain arrays/objects.

Set

Definition: Set is a collection of unique values, similar to a mathematical set.

Characteristics:

  • Stored values are unique
  • Equality check uses === semantics for most values
  • Keeps insertion order
  • Can store any value type (primitive or object)

Basic usage:

// create Set
const set = new Set();

// add values
set.add(1);
set.add(2);
set.add(2); // duplicate ignored
set.add('hello');
set.add({ name: 'John' });

console.log(set.size); // 4
console.log(set); // Set(4) { 1, 2, 'hello', { name: 'John' } }

// existence check
console.log(set.has(1)); // true
console.log(set.has(3)); // false

// delete
set.delete(2);
console.log(set.has(2)); // false

// clear
set.clear();
console.log(set.size); // 0

Create Set from array:

// remove duplicates from array
const arr = [1, 2, 2, 3, 3, 3];
const uniqueSet = new Set(arr);
console.log(uniqueSet); // Set(3) { 1, 2, 3 }

// convert back to array
const uniqueArr = [...uniqueSet];
console.log(uniqueArr); // [1, 2, 3]

// shorthand
const uniqueArr2 = [...new Set(arr)];

Map

Definition: Map is a key-value collection similar to objects, but keys can be any type.

Characteristics:

  • Keys can be any type (string, number, object, function, etc.)
  • Keeps insertion order
  • Has size property
  • Iteration follows insertion order

Basic usage:

// create Map
const map = new Map();

// set key-value pairs
map.set('name', 'John');
map.set(1, 'one');
map.set(true, 'boolean');
map.set({ id: 1 }, 'object key');

// get value
console.log(map.get('name')); // 'John'
console.log(map.get(1)); // 'one'

// key existence
console.log(map.has('name')); // true

// delete
map.delete('name');

// size
console.log(map.size); // 3

// clear
map.clear();

Create Map from array:

// from 2D array
const entries = [
['name', 'John'],
['age', 30],
['city', 'Taipei'],
];
const map = new Map(entries);
console.log(map.get('name')); // 'John'

// from object
const obj = { name: 'John', age: 30 };
const map2 = new Map(Object.entries(obj));
console.log(map2.get('name')); // 'John'

2. Set vs Array

Difference between Set and Array

FeatureSetArray
DuplicatesNot allowedAllowed
Index accessNot supportedSupported
Lookup complexityO(1) averageO(n)
Insertion orderPreservedPreserved
Common methodsadd, has, deletepush, pop, indexOf

Use cases:

// ✅ Set for unique values
const userIds = new Set([1, 2, 3, 2, 1]);
console.log([...userIds]); // [1, 2, 3]

// ✅ Set for fast existence checks
const visitedPages = new Set();
visitedPages.add('/home');
visitedPages.add('/about');
if (visitedPages.has('/home')) {
console.log('Homepage already visited');
}

// ✅ Array when index or duplicates are needed
const scores = [100, 95, 100, 90];
console.log(scores[0]); // 100

3. Map vs Object

Difference between Map and Object

FeatureMapObject
Key typeAny typeString or Symbol
Sizesize propertyManual calculation
Default keysNonePrototype chain exists
Iteration orderInsertion orderInsertion order in modern JS
PerformanceBetter for frequent add/removeOften good for static/simple structures
JSONNot directly serializableNative JSON support

Use cases:

// ✅ Map when keys are non-string
const userMetadata = new Map();
const user1 = { id: 1 };
const user2 = { id: 2 };

userMetadata.set(user1, { lastLogin: '2024-01-01' });
userMetadata.set(user2, { lastLogin: '2024-01-02' });

console.log(userMetadata.get(user1)); // { lastLogin: '2024-01-01' }

// ✅ Map for frequent add/remove
const cache = new Map();
cache.set('key1', 'value1');
cache.delete('key1');
cache.set('key2', 'value2');

// ✅ Object for static structure + JSON
const config = {
apiUrl: 'https://api.example.com',
timeout: 5000,
};
const json = JSON.stringify(config);

4. Common Interview Questions

Common interview questions

Question 1: remove duplicates from array

Implement a function to remove duplicates.

function removeDuplicates(arr) {
// your implementation
}
Click to view answer

Method 1: Set (recommended)

function removeDuplicates(arr) {
return [...new Set(arr)];
}

console.log(removeDuplicates([1, 2, 2, 3, 3, 3])); // [1, 2, 3]
console.log(removeDuplicates(['a', 'b', 'a', 'c'])); // ['a', 'b', 'c']

Method 2: filter + indexOf

function removeDuplicates(arr) {
return arr.filter((value, index) => arr.indexOf(value) === index);
}

Method 3: reduce

function removeDuplicates(arr) {
return arr.reduce((acc, value) => {
if (!acc.includes(value)) {
acc.push(value);
}
return acc;
}, []);
}

Performance:

  • Set: O(n), fastest
  • filter + indexOf: O(n²), slower
  • reduce + includes: O(n²), slower

Question 2: check duplicates in array

Implement a function to check whether an array has duplicates.

function hasDuplicates(arr) {
// your implementation
}
Click to view answer

Method 1: Set (recommended)

function hasDuplicates(arr) {
return new Set(arr).size !== arr.length;
}

console.log(hasDuplicates([1, 2, 3])); // false
console.log(hasDuplicates([1, 2, 2, 3])); // true

Method 2: Set with early exit

function hasDuplicates(arr) {
const seen = new Set();
for (const value of arr) {
if (seen.has(value)) {
return true;
}
seen.add(value);
}
return false;
}

Method 3: indexOf

function hasDuplicates(arr) {
return arr.some((value, index) => arr.indexOf(value) !== index);
}

Performance:

  • Set method 1: O(n), fastest
  • Set method 2: O(n), can stop early
  • indexOf: O(n²), slower

Question 3: count occurrences

Implement a function to count occurrences of each element.

function countOccurrences(arr) {
// your implementation
}
Click to view answer

Method 1: Map (recommended)

function countOccurrences(arr) {
const map = new Map();

for (const value of arr) {
map.set(value, (map.get(value) || 0) + 1);
}

return map;
}

const arr = ['a', 'b', 'a', 'c', 'b', 'a'];
const counts = countOccurrences(arr);
console.log(counts.get('a')); // 3
console.log(counts.get('b')); // 2
console.log(counts.get('c')); // 1

Method 2: reduce + Map

function countOccurrences(arr) {
return arr.reduce((map, value) => {
map.set(value, (map.get(value) || 0) + 1);
return map;
}, new Map());
}

Method 3: plain object

function countOccurrences(arr) {
const counts = {};
for (const value of arr) {
counts[value] = (counts[value] || 0) + 1;
}
return counts;
}

const arr = ['a', 'b', 'a', 'c', 'b', 'a'];
const counts = countOccurrences(arr);
console.log(counts); // { a: 3, b: 2, c: 1 }

Why Map helps:

  • Keys can be any type
  • Built-in size
  • Stable insertion-order iteration

Question 4: intersection of two arrays

Implement array intersection.

function intersection(arr1, arr2) {
// your implementation
}
Click to view answer

Method 1: Set

function intersection(arr1, arr2) {
const set1 = new Set(arr1);
const set2 = new Set(arr2);
const result = [];

for (const value of set1) {
if (set2.has(value)) {
result.push(value);
}
}

return result;
}

console.log(intersection([1, 2, 3], [2, 3, 4])); // [2, 3]

Method 2: filter + Set

function intersection(arr1, arr2) {
const set2 = new Set(arr2);
return [...new Set(arr1)].filter((value) => set2.has(value));
}

Method 3: filter + includes

function intersection(arr1, arr2) {
return arr1.filter((value) => arr2.includes(value));
}

Performance:

  • Set: O(n + m), faster
  • filter + includes: O(n × m), slower

Question 5: difference of two arrays

Implement array difference (values in arr1 but not in arr2).

function difference(arr1, arr2) {
// your implementation
}
Click to view answer

Method 1: Set

function difference(arr1, arr2) {
const set2 = new Set(arr2);
return arr1.filter((value) => !set2.has(value));
}

console.log(difference([1, 2, 3, 4], [2, 3])); // [1, 4]

Method 2: dedupe first then filter

function difference(arr1, arr2) {
const set1 = new Set(arr1);
const set2 = new Set(arr2);
return [...set1].filter((value) => !set2.has(value));
}

Method 3: includes

function difference(arr1, arr2) {
return arr1.filter((value) => !arr2.includes(value));
}

Performance:

  • Set: O(n + m), faster
  • includes: O(n × m), slower

Question 6: implement LRU cache

Implement an LRU cache with Map.

class LRUCache {
constructor(capacity) {
// your implementation
}

get(key) {
// your implementation
}

put(key, value) {
// your implementation
}
}
Click to view answer

Implementation:

class LRUCache {
constructor(capacity) {
this.capacity = capacity;
this.cache = new Map();
}

get(key) {
if (!this.cache.has(key)) {
return -1;
}

// move key to end (most recently used)
const value = this.cache.get(key);
this.cache.delete(key);
this.cache.set(key, value);

return value;
}

put(key, value) {
// if key exists, delete first
if (this.cache.has(key)) {
this.cache.delete(key);
}
// if full, evict oldest (first key)
else if (this.cache.size >= this.capacity) {
const firstKey = this.cache.keys().next().value;
this.cache.delete(firstKey);
}

// add to end (most recent)
this.cache.set(key, value);
}
}

// usage
const cache = new LRUCache(2);
cache.put(1, 'one');
cache.put(2, 'two');
console.log(cache.get(1)); // 'one'
cache.put(3, 'three'); // evicts key 2
console.log(cache.get(2)); // -1
console.log(cache.get(3)); // 'three'

Explanation:

  • Map keeps insertion order
  • first key is the oldest
  • get refreshes recency by delete+set
  • put evicts oldest when capacity is full

Question 7: object as Map key

Explain the output:

const map = new Map();
const obj1 = { id: 1 };
const obj2 = { id: 1 };

map.set(obj1, 'first');
map.set(obj2, 'second');

console.log(map.get(obj1));
console.log(map.get(obj2));
console.log(map.size);
Click to view answer
// 'first'
// 'second'
// 2

Explanation:

  • obj1 and obj2 are different object instances
  • Map compares object keys by reference
  • so they are treated as different keys

Contrast with plain object:

// plain object keys become strings
const obj = {};
const obj1 = { id: 1 };
const obj2 = { id: 1 };

obj[obj1] = 'first';
obj[obj2] = 'second';

console.log(obj[obj1]); // 'second' (overwritten)
console.log(obj[obj2]); // 'second'
console.log(Object.keys(obj)); // ['[object Object]']

// Map keeps references
const map = new Map();
map.set(obj1, 'first');
map.set(obj2, 'second');

console.log(map.get(obj1)); // 'first'
console.log(map.get(obj2)); // 'second'
console.log(map.size); // 2

5. WeakSet and WeakMap

Difference between WeakSet and WeakMap

WeakSet

Characteristics:

  • Can only store objects
  • Uses weak references
  • No size
  • Not iterable
  • No clear

Use case: object marking without memory leaks.

const weakSet = new WeakSet();

const obj1 = { id: 1 };
const obj2 = { id: 2 };

weakSet.add(obj1);
weakSet.add(obj2);

console.log(weakSet.has(obj1)); // true

// if obj1 has no other references, it can be garbage-collected

WeakMap

Characteristics:

  • Keys must be objects
  • Uses weak references for keys
  • No size
  • Not iterable
  • No clear

Use case: private metadata tied to object lifecycle.

const weakMap = new WeakMap();

const obj1 = { id: 1 };
const obj2 = { id: 2 };

weakMap.set(obj1, 'data1');
weakMap.set(obj2, 'data2');

console.log(weakMap.get(obj1)); // 'data1'

// if obj1 has no other references, entry can be garbage-collected

Weak vs strong collections

FeatureSet/MapWeakSet/WeakMap
Key/value typeAny typeObject only
Weak referenceNoYes
IterableYesNo
sizeYesNo
clearYesNo
Auto GC cleanupNoYes

6. Best Practices

Best practices

// 1. Use Set for uniqueness
const uniqueIds = new Set([1, 2, 3, 2, 1]);
console.log([...uniqueIds]); // [1, 2, 3]

// 2. Use Set for fast lookup
const allowedUsers = new Set(['user1', 'user2', 'user3']);
if (allowedUsers.has(currentUser)) {
// access granted
}

// 3. Use Map when keys are not strings
const metadata = new Map();
const user = { id: 1 };
metadata.set(user, { lastLogin: new Date() });

// 4. Use Map for frequent add/remove
const cache = new Map();
cache.set('key', 'value');
cache.delete('key');

// 5. Use WeakMap for object-linked private data
const privateData = new WeakMap();
class User {
constructor(name) {
privateData.set(this, { name });
}
getName() {
return privateData.get(this).name;
}
}

Avoid

// 1. Do not use Set as full array replacement
const set = new Set([1, 2, 3]);
// set[0] -> undefined

const arr = [1, 2, 3];
arr[0]; // 1

// 2. Do not use Map for simple static structure
const configMap = new Map();
configMap.set('apiUrl', 'https://api.example.com');
configMap.set('timeout', 5000);

const config = {
apiUrl: 'https://api.example.com',
timeout: 5000,
};

// 3. Do not confuse Set and Map APIs
const set2 = new Set();
// set2.set('key', 'value'); // TypeError

const map = new Map();
map.set('key', 'value');

7. Interview Summary

Interview summary

Quick memory

Set:

  • Unique values only
  • Fast lookup O(1)
  • Great for deduplication and membership checks

Map:

  • Key-value store with keys of any type
  • Preserves insertion order
  • Great for non-string keys and frequent updates

WeakSet / WeakMap:

  • Weak references, GC-friendly
  • Object-only keys/values
  • Great for leak-safe object metadata

Sample interview answers

Q: When should you use Set instead of Array?

Use Set when you need uniqueness or fast existence checks. Set.has is O(1) on average, while array includes is O(n). Typical examples are deduplication and permission checks.

Q: What is the difference between Map and Object?

Map keys can be any type, including objects/functions. Object keys are only string or Symbol. Map has size, preserves insertion order, and avoids prototype-chain key issues. Map is better when keys are dynamic or updates are frequent.

Q: What is the difference between WeakMap and Map?

WeakMap keys must be objects and are weakly referenced. If the key object is no longer referenced elsewhere, its entry can be garbage-collected automatically. WeakMap is not iterable and has no size. It is useful for private data and memory-leak prevention.

Reference