How to Create a Shallow or Deep Copy of a Map or Set in JavaScript
When working with Map and Set objects in JavaScript, you often need to create a copy. A critical distinction to make is whether you need a shallow copy or a deep copy. A shallow copy duplicates the collection but not its nested values, while a deep copy duplicates everything, ensuring complete independence.
This guide will teach you the modern, idi-omatic methods for creating both shallow and deep copies of Map and Set objects, explaining the important differences and helping you choose the right approach for your needs.
Shallow Copy vs. Deep Copy: The Core Concept
Understanding this difference is essential to avoid bugs.
- Shallow Copy: Creates a new
MaporSet, but the values inside it (if they are objects or arrays) are references to the same values in the original. Modifying a nested object in the copy will also modify it in the original. - Deep Copy: Creates a new
MaporSetand recursively creates new copies of all nested objects and arrays. The copy is completely independent of the original.
Consider this example:
const originalMap = new Map([
['user', { name: 'Alice', roles: ['admin'] }]
]);
If we make a shallow copy and change a role, the original is also affected. If we make a deep copy, it is not. This is why the distinction is so important.
How to Create a Shallow Copy of a Map or Set
The easiest and most idiomatic way to create a shallow copy of a Map or Set is to pass the original collection directly to its constructor.
This solution works because the Map and Set constructors can accept another Map or Set as an iterable to create a new instance from.
For a Map:
const originalMap = new Map([['a', 1], ['b', 2]]);
const shallowCopy = new Map(originalMap);
shallowCopy.set('c', 3); // Modify the copy
console.log(originalMap.has('c')); // Output: false (The original is unaffected)
console.log(shallowCopy.has('c')); // Output: true
For a Set:
const originalSet = new Set([1, 2, 3]);
const shallowCopy = new Set(originalSet);
shallowCopy.add(4); // Modify the copy
console.log(originalSet.has(4)); // Output: false (The original is unaffected)
console.log(shallowCopy.has(4)); // Output: true
How to Create a Deep Copy of a Map or Set
Natively, JavaScript does not have a simple, built-in function to perform a deep copy. The standard, idiomatic workaround for this is the "JSON trick": JSON.parse(JSON.stringify(value)).
The logic:
- Convert the
MaporSetto an array so it can be stringified. JSON.stringify(): Serializes the data into a JSON string. This process discards all object references.JSON.parse(): Parses the JSON string, creating brand new objects and arrays for all nested data.- Pass the resulting plain JavaScript array back to the
MaporSetconstructor.
The solution (for a Map):
const originalMap = new Map([
['user', { name: 'Alice', roles: ['admin'] }]
]);
// Convert to array -> stringify -> parse -> convert back to Map
const deepCopy = new Map(
JSON.parse(JSON.stringify(Array.from(originalMap)))
);
The solution (for a Set):
const originalSet = new Set([ { id: 1 }, { id: 2 } ]);
// Convert to array -> stringify -> parse -> convert back to Set
const deepCopy = new Set(
JSON.parse(JSON.stringify(Array.from(originalSet)))
);
Limitation: This JSON trick will fail on Maps or Sets that contain non-serializable values like functions, undefined, or Symbols. For those complex cases, a dedicated library like lodash's cloneDeep() is the best solution.
Why Deep Copying is Necessary
Let's revisit our initial problem to see why a shallow copy can be dangerous.
The Problem with Shallow Copying
const originalMap = new Map([
['user', { name: 'Alice', roles: ['admin'] }]
]);
// Create a shallow copy
const shallowCopy = new Map(originalMap);
// Modify a nested object in the COPY
shallowCopy.get('user').name = 'Bob';
// The ORIGINAL is also changed!
console.log(originalMap.get('user').name);
// Output: "Bob" (This is the bug!)
This happens because both originalMap and shallowCopy contain a reference to the exact same user object.
The Deep Copy Fix
const originalMap = new Map([
['user', { name: 'Alice', roles: ['admin'] }]
]);
// Create a deep copy
const deepCopy = new Map(JSON.parse(JSON.stringify(Array.from(originalMap))));
// Modify a nested object in the DEEP COPY
deepCopy.get('user').name = 'Bob';
// The ORIGINAL remains unchanged
console.log(originalMap.get('user').name);
// Output: "Alice" (Correct!)
Conclusion
Choosing between a shallow and deep copy is a critical decision in JavaScript.
- To create a shallow copy, the best and simplest method is to use the constructor:
new Map(originalMap)ornew Set(originalSet). This is fast and sufficient when your collections contain only primitive values. - To create a deep copy, the standard "JSON trick" is the most common native solution:
JSON.parse(JSON.stringify(Array.from(collection))). Use this whenever your collections contain nested objects or arrays to ensure the copy is completely independent.