Lesson 3
Understanding and Using JavaScript Maps
Introduction

Welcome to our JavaScript data structures revision! Today, we will delve deeply into JavaScript Maps. Much like a bookshelf, Maps allow you to quickly select the book (value) you desire by reading its label (key). They are vital to JavaScript for quickly accessing values using keys and for efficient key insertion and deletion. So, let's explore JavaScript Maps for a clearer understanding of these concepts.

JavaScript Maps

Our journey starts with JavaScript Maps, a pivotal data structure that holds data as key-value pairs. Imagine storing your friend's contact info in such a way that allows you to search for your friend's name (the key) and instantly find their phone number (the value).

JavaScript
1class PhoneBook { 2 3 constructor() { 4 // An empty Map 5 this.contacts = new Map(); 6 } 7 8 addContact(name, phoneNumber) { 9 // Method to add a contact 10 this.contacts.set(name, phoneNumber); 11 } 12 13 getPhoneNumber(name) { 14 // Method to retrieve contact's phone number, or undefined if it's not in contacts 15 return this.contacts.get(name); 16 } 17} 18 19// Create a PhoneBook instance 20const phoneBook = new PhoneBook(); 21 22// Add contacts 23phoneBook.addContact("Alice", "123-456-7890"); 24phoneBook.addContact("Bob", "234-567-8901"); 25console.log(phoneBook.getPhoneNumber("Alice")); // Output: "123-456-7890" 26console.log(phoneBook.getPhoneNumber("Bobby")); // Output: undefined

In the above code, we create a PhoneBook class that uses a Map to store contacts. As you can see, Maps simplify the processes of adding, modifying, and accessing information with unique keys.

Operations in Maps

JavaScript Maps enable a variety of operations for manipulating data, such as setting, getting, deleting key-value pairs, and more. Understanding these operations is crucial for efficient data handling in JavaScript.

To add or update entries in a Map, you use the set method. If the key exists, the value is updated; if not, a new key-value pair is added. This flexibility allows for dynamic updates and additions to the Map without needing a predefined structure.

The get method is used to retrieve the value associated with a specific key. It provides a safe way to access values, as attempting to access a non-existent key simply returns undefined.

Checking if a key exists in the Map can be done using the has method. This method returns a boolean value—true if the key exists in the Map, otherwise false. This is particularly useful for conditionally handling data based on its existence in the Map.

Deleting an entry is done using the delete method followed by the key. This operation removes the specified key-value pair from the Map, which is essential for managing the contents of the Map actively.

The size property provides the number of key-value pairs present in the Map. This is especially useful when you need to know the total count of entries in the Map.

Let’s see how these operations work in the context of a TaskManager class:

JavaScript
1class TaskManager { 2 3 constructor() { 4 // Initialize with an empty Map 5 this.tasks = new Map(); 6 } 7 8 addUpdateTask(taskName, status) { 9 // Add a new task or update an existing task 10 this.tasks.set(taskName, status); 11 } 12 13 getTaskStatus(taskName) { 14 // Retrieve the status of a task; Returns "Not Found" if the task does not exist 15 return this.tasks.get(taskName) !== undefined ? this.tasks.get(taskName) : "Not Found"; 16 } 17 18 deleteTask(taskName) { 19 // Removes a task using its name 20 if (this.tasks.has(taskName)) { 21 this.tasks.delete(taskName); 22 } else { 23 console.log(`Task '${taskName}' not found.`); 24 } 25 } 26 27 getTaskCount() { 28 // Returns the number of tasks in the TaskManager 29 return this.tasks.size; 30 } 31} 32 33// Test the TaskManager class 34const myTasks = new TaskManager(); 35myTasks.addUpdateTask("Buy Milk", "Pending"); 36console.log(myTasks.getTaskStatus("Buy Milk")); // Output: Pending 37myTasks.addUpdateTask("Buy Milk", "Completed"); 38console.log(myTasks.getTaskStatus("Buy Milk")); // Output: Completed 39 40myTasks.deleteTask("Buy Milk"); 41console.log(myTasks.getTaskStatus("Buy Milk")); // Output: Not Found 42 43myTasks.addUpdateTask("Clean House", "In Progress"); 44console.log(myTasks.getTaskCount()); // Output: 1

This example showcases how to leverage Map operations in JavaScript to effectively manage data by adding, updating, retrieving, deleting entries, and checking the number of entries through a simulated Task Manager application.

Looping Through Maps

JavaScript provides an elegant way to loop through Maps using for...of loops. We can iterate through keys, values, or both simultaneously using specific functions provided by the Map object.

  • keys(): This function returns an iterator object containing all the keys in the Map.
  • values(): This function returns an iterator object containing all the values in the Map.
  • entries(): This function returns an iterator object containing arrays of [key, value] pairs for each entry in the Map.

Let's explore this in our Task Manager example:

JavaScript
1class TaskManager { 2 constructor() { 3 this.tasks = new Map(); 4 } 5 6 addTask(taskName, status) { 7 this.tasks.set(taskName, status); 8 } 9 10 printAllTaskKeys() { 11 // Prints all tasks' keys 12 for (let taskName of this.tasks.keys()) { 13 console.log(taskName); 14 } 15 } 16 17 printAllTaskValues() { 18 // Prints all tasks' values (statuses) 19 for (let status of this.tasks.values()) { 20 console.log(status); 21 } 22 } 23 24 printAllEntries() { 25 // Prints all tasks as [key, value] pairs 26 for (let [taskName, status] of this.tasks.entries()) { 27 console.log(`${taskName}: ${status}`); 28 } 29 } 30} 31 32const myTasks = new TaskManager(); 33myTasks.addTask("Buy Milk", "Pending"); 34myTasks.addTask("Pay Bills", "Completed"); 35 36myTasks.printAllTaskKeys(); 37/* Output: 38Buy Milk 39Pay Bills 40*/ 41 42myTasks.printAllTaskValues(); 43/* Output: 44Pending 45Completed 46*/ 47 48myTasks.printAllEntries(); 49/* Output: 50Buy Milk: Pending 51Pay Bills: Completed 52*/

In this case, we demonstrate how to use for...of loops with the keys(), values(), and entries() functions to iterate over a Map's keys, values, and entries respectively. This allows us to flexibly print all tasks in our task manager along with their statuses.

Nesting with Maps

Nesting in Maps involves storing Maps within another Map. It's useful when associating multiple pieces of information with a key. Let's see how this works in a Student Database example.

JavaScript
1class StudentDatabase { 2 constructor() { 3 this.students = new Map(); 4 } 5 6 addStudent(name, subjects) { 7 // Adds students and subjects 8 this.students.set(name, new Map(Object.entries(subjects))); 9 } 10 11 getMark(name, subject) { 12 const studentSubjects = this.students.get(name); 13 return studentSubjects ? (studentSubjects.get(subject) || "N/A") : "N/A"; 14 } 15 16 printDatabase() { 17 // Prints student and their subjects with grades 18 for (let [name, subjects] of this.students.entries()) { 19 console.log("Student:", name); 20 for (let [subject, grade] of subjects.entries()) { 21 console.log(` Subject: ${subject}, Grade: ${grade}`); 22 } 23 } 24 } 25} 26 27// Create a StudentDatabase instance 28const studentDb = new StudentDatabase(); 29studentDb.addStudent("Alice", { Math: "A", English: "B" }); 30 31console.log(studentDb.getMark("Alice", "English")); // Output: "B" 32console.log(studentDb.getMark("Alice", "History")); // Output: "N/A" 33studentDb.printDatabase(); 34/* Output: 35Student: Alice 36 Subject: Math, Grade: A 37 Subject: English, Grade: B 38*/
Hands-on Example

Let's shift our focus to a more interactive and familiar scenario: managing a shopping cart in an online store. This hands-on example will demonstrate how Maps can be used to map product names to their quantities in a shopping cart. You will learn how to add products, update quantities, and retrieve the total number of items in the cart.

Here’s how you can implement and manipulate a shopping cart using a JavaScript Map:

JavaScript
1class ShoppingCart { 2 3 constructor() { 4 // Initialize cart as an empty Map 5 this.cart = new Map(); 6 } 7 8 addProduct(productName, quantity) { 9 // Add or update the quantity of a product in the cart 10 if (this.cart.has(productName)) { 11 this.cart.set(productName, this.cart.get(productName) + quantity); 12 } else { 13 this.cart.set(productName, quantity); 14 } 15 } 16 17 removeProduct(productName) { 18 // Remove a product from the cart 19 if (this.cart.has(productName)) { 20 this.cart.delete(productName); 21 } else { 22 console.log(`${productName} not found in your cart.`); 23 } 24 } 25 26 showCart() { 27 // Display the products and their quantities in the cart 28 if (this.cart.size === 0) { 29 console.log("Your shopping cart is empty."); 30 } else { 31 for (let [product, quantity] of this.cart.entries()) { 32 console.log(`${product}: ${quantity}`); 33 } 34 } 35 } 36} 37 38// Create an instance of ShoppingCart 39const myCart = new ShoppingCart(); 40 41// Add products and update their quantities 42myCart.addProduct("Apples", 5); 43myCart.addProduct("Bananas", 2); 44myCart.addProduct("Apples", 3); // Updates quantity of apples to 8 45 46// Display cart 47myCart.showCart(); 48/* Output: 49Apples: 8 50Bananas: 2 51*/ 52 53// Remove a product and show the updated cart 54myCart.removeProduct("Bananas"); 55myCart.showCart(); 56/* Output: 57Apples: 8 58*/

This example showcases the practical application of Maps to manage a dynamic dataset, such as an online shopping cart. By using product names as keys and their quantities as values, we achieve efficient and flexible data manipulation. This exercise provides a solid foundation for understanding how to handle complex data structures in real-world JavaScript applications.

Lesson Summary and Practice

Well done! Today, we delved into JavaScript Maps and explored various operations on Maps. We now invite you to get hands-on experience with the upcoming practice exercises. To master these concepts and hone your JavaScript Map skills, practice is key. Happy learning!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.