DEV Community

Cover image for What Are the Differences Between Map and Set in JavaScript, and Why Is a Deep Copy Not Possible with the Spread (...) Operator?
Abdulnasır Olcan
Abdulnasır Olcan

Posted on

What Are the Differences Between Map and Set in JavaScript, and Why Is a Deep Copy Not Possible with the Spread (...) Operator?

JavaScript offers powerful data structures and flexible language features. Two of these features are the Map and Set data structures. Although they are used for similar purposes, there are significant differences between Map and Set. In this article, we will explore the key differences between these two structures. Additionally, we will evaluate the copying capabilities of the commonly used spread (...) operator in JavaScript, specifically in the context of Map and Set, and explain why a deep copy is not achievable.

Differences Between Map and Set in JavaScript

In JavaScript, the Map and Set data structures are used to store data, but they have some key differences in terms of functionality and usage. Here are the fundamental differences between Map and Set:

  1. Definition and Purpose:
  • Map:

    A Map is used to store data in the form of key-value pairs. It functions similarly to an object, but any data type (object, function, primitive) can be used as a key.

    The value of a key is stored uniquely in a Map.

    Maps allow us to establish one-to-one relationships between keys and values.

  • Set:

    A Set stores a collection of unique values. Each value can only appear once, regardless of its data type.

    Sets are similar to arrays, but they cannot contain duplicate values.

    In a Set, only values are stored; there are no keys.

  1. Structure:
    • Map: A Map structure consists of each element being a key and a corresponding value.

const myMap = new Map();
myMap.set('key1', 'value1');
myMap.set('key2', 'value2');
console.log(myMap.get('key1')); // "value1"

Enter fullscreen mode Exit fullscreen mode
  • Set: In a Set structure, each element is just a value, and these values are unique.

const mySet = new Set();
mySet.add('value1');
mySet.add('value2');
mySet.add('value1'); // The duplicate value is not added.
console.log(mySet.size); // 2

Enter fullscreen mode Exit fullscreen mode

3. Usage of Key and Value:

  • Map: Any data type can be used as a key (string, number, object, function, etc.).

const myMap = new Map();
const objKey = { id: 1 };
myMap.set(objKey, 'Object as a key');
console.log(myMap.get(objKey)); // "Object as a key"

Enter fullscreen mode Exit fullscreen mode
  • Set: Only values are stored in a Set. There is no key-value structure.

const mySet = new Set();
mySet.add({ id: 1 });
console.log(mySet.has({ id: 1 })); // false, because it is a different reference.

Enter fullscreen mode Exit fullscreen mode

4. Duplicate Values:

  • Map: The same key can be assigned a different value, in which case the previous value is overwritten.

const myMap = new Map();
myMap.set('key', 'value1');
myMap.set('key', 'value2'); // Assigns a new value for the "key".
console.log(myMap.get('key')); // "value2"

Enter fullscreen mode Exit fullscreen mode
  • Set: A Set cannot contain multiple instances of the same value. If a duplicate value is added, it is ignored.

const mySet = new Set();
mySet.add('value');
mySet.add('value'); // The same value is not added again.
console.log(mySet.size); // 1

Enter fullscreen mode Exit fullscreen mode

5. Performance:

  • Map: Operations like adding data, retrieving (get), and deleting (delete) are very fast with Maps. Maps are especially advantageous in terms of performance when using an object as a key.

  • Set: Set also offers high performance in add, has, and delete operations. It is ideal for creating and checking collections of unique values.

6. Iteration:

  • Map: In Maps, you can iterate over keys (keys()), values (values()), and both together (entries()).

const myMap = new Map();
myMap.set('a', 1);
myMap.set('b', 2);

for (let [key, value] of myMap) {
  console.log(key, value);
}
// Output:
// "a" 1
// "b" 2

Enter fullscreen mode Exit fullscreen mode
  • Set: In Sets, you can only iterate over the values (using values() or keys()). Sets also provide an entries() method, but this method returns the same value for both key and value.

const mySet = new Set([1, 2, 3]);

for (let value of mySet) {
  console.log(value);
}
// Output:
// 1
// 2
// 3

Enter fullscreen mode Exit fullscreen mode

7. Use Cases:

  • Map:

    If you want to maintain a key-value relationship in a data set (e.g., storing user information by IDs), using a Map is more suitable.

    Maps are much more efficient when you need to use objects as keys.

  • Set:

    If you need a list consisting only of unique values, using a Set is ideal.

    For example, to remove all duplicate items from an array:


const numbers = [1, 2, 2, 3, 4, 4];
const uniqueNumbers = [...new Set(numbers)];
console.log(uniqueNumbers); // [1, 2, 3, 4]

Enter fullscreen mode Exit fullscreen mode

Summary:

Image description

These differences can help you understand when to use Map and Set. Both of these data structures are very useful for specific use cases in JavaScript projects.

Why Is a Deep Copy Not Possible with the Spread (...) Operator?

The ... (spread) operator in JavaScript does not create a deep copy; it creates a shallow copy.

What Is the Spread Operator?

The spread operator is used to copy an array or object into another array or object. However, this copying process only copies the surface-level data. This means that if the copied data contains nested arrays or objects, the inner objects or arrays share the same reference.

What Is a Shallow Copy?

A shallow copy copies only the first level of an object or array. If there are other objects or arrays nested inside, their references are copied, meaning that the original and the copied object both point to the same inner object.

What Is a Deep Copy?

A deep copy, on the other hand, creates completely new copies of all nested structures inside an object or array. This means there is no reference sharing between the original and the copied structure. Each data structure is independent of the other.

Explanation with Examples:

  1. Simple Array and Object (Shallow Copy): We can use the spread operator to copy a simple array or object:

const arr1 = [1, 2, 3];
const arr2 = [...arr1];
arr2.push(4);

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

Enter fullscreen mode Exit fullscreen mode

In this example, arr1 and arr2 are independent. Changes made to arr2 do not affect arr1 because a simple array was copied.

  1. Nested Object or Array (Shallow Copy Issue): However, when objects or arrays are nested, the spread operator copies the references:

const obj1 = {
  name: "Abdulnasır",
  address: { city: "Istanbul" }
};

const obj2 = { ...obj1 };
obj2.address.city = "Antalya";

console.log(obj1.address.city); // "Antalya"
console.log(obj2.address.city); // "Antalya"

Enter fullscreen mode Exit fullscreen mode

In this example, when obj2 is created, the reference of the address object is copied. Therefore, changes made to obj2 also affect obj1. In other words, obj1 and obj2 share the same address object.

  1. Using the JSON Method for Deep Copy: To create a deep copy, methods like JSON.parse(JSON.stringify(...)) can be used:

const obj1 = {
  name: "Abdulnasır",
  address: { city: "Istanbul" }
};

const obj2 = JSON.parse(JSON.stringify(obj1));
obj2.address.city = "Antalya";

console.log(obj1.address.city); // "Istanbul"
console.log(obj2.address.city); // "Antalya"

Enter fullscreen mode Exit fullscreen mode

In this method, we convert obj1 into a JSON string using JSON.stringify and then turn it back into an object using JSON.parse. This way, obj1 and obj2 become completely independent objects, and changes made to obj2 do not affect obj1.

Map and the Spread Operator:

You can use the spread operator to copy a Map:


const originalMap = new Map([['key1', 'value1'], ['key2', 'value2']]);
const copiedMap = new Map([...originalMap]);
copiedMap.set('key3', 'value3');

console.log(originalMap.has('key3')); // false
console.log(copiedMap.has('key3')); // true

Enter fullscreen mode Exit fullscreen mode

In this example, copiedMap is a shallow copy of originalMap. However, if there are nested data structures, these inner structures will share the same reference.

Set and the Spread Operator:

Similarly, Sets can also be copied using the spread operator:


const originalSet = new Set([1, 2, 3]);
const copiedSet = new Set([...originalSet]);
copiedSet.add(4);

console.log(originalSet.has(4)); // false
console.log(copiedSet.has(4)); // true

Enter fullscreen mode Exit fullscreen mode

Can a Deep Copy Be Made with the Spread Operator?

The spread operator only copies the first level of data and assigns references to the new variable. In nested data structures, references are preserved, which means that a deep copy is not performed:

Deep Copy Example:


const nestedMap = new Map([['key1', { innerKey: 'innerValue' }]]);
const copiedNestedMap = new Map([...nestedMap]);

copiedNestedMap.get('key1').innerKey = 'newValue';

console.log(nestedMap.get('key1').innerKey); // "newValue"

Enter fullscreen mode Exit fullscreen mode

Here is the translation:

In this example, changing innerKey inside copiedNestedMap also affects nestedMap, because the inner object shares the same reference. In this case, a deep copy would have been necessary.

JSON Approach for Deep Copy (Limited):

Using the JSON method, it is possible to make deep copies for objects, but it cannot be applied directly to special data structures like Map and Set. This is because the JSON format does not support their unique key-value mappings and methods.

Summary:

  • Spread Operator (...): Creates a shallow copy, meaning it only copies the first-level values.
  • Shallow Copy: Nested objects or arrays share the same reference.
  • Deep Copy: All nested structures are created as entirely new copies, with no reference sharing.

Conclusion:

Using Map and Set for data storage and management in JavaScript is highly flexible and powerful. However, it is important to remember that when copying with the ... (spread) operator, only a shallow copy is made, and additional methods are needed for deep copying.

Happy coding! 🚀

Top comments (0)