Lesson 3
Data Structures in C#: Exploring Dictionaries
Data Structures in C#: Exploring Dictionaries

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

Introduction to C# Dictionaries and Operations

Before diving into real-world applications, it’s essential to grasp the fundamentals of C# Dictionaries, a crucial data structure for storing data as key-value pairs. Understanding how to define and perform basic operations on them prepares us for more complex implementations.

In C#, keys in a Dictionary must be unique and immutable. Common types used as keys include strings, integers, enums, and any object that overrides the GetHashCode() and Equals() methods. This ensures the keys are suitable for fast lookups.

C#
1using System; 2using System.Collections.Generic; 3 4// Defining a Dictionary 5Dictionary<string, int> ageDictionary = new Dictionary<string, int>(); 6 7// Adding entries 8ageDictionary["Alice"] = 25; 9ageDictionary["Bob"] = 30; 10Console.WriteLine("After adding: "); 11foreach (var entry in ageDictionary) 12{ 13 Console.WriteLine($"{entry.Key}: {entry.Value}"); 14} 15 16// Updating entries 17ageDictionary["Alice"] = 26; // Updates Alice's age 18Console.WriteLine("\nAfter updating: "); 19foreach (var entry in ageDictionary) 20{ 21 Console.WriteLine($"{entry.Key}: {entry.Value}"); 22} 23 24// Retrieving values 25int ageOfAlice = ageDictionary.ContainsKey("Alice") ? ageDictionary["Alice"] : -1; 26Console.WriteLine($"\nAlice's Age: {ageOfAlice}"); 27 28// Removing entries 29ageDictionary.Remove("Bob"); 30Console.WriteLine("\nAfter removing Bob: "); 31foreach (var entry in ageDictionary) 32{ 33 Console.WriteLine($"{entry.Key}: {entry.Value}"); 34} 35 36// Counting entries 37int entryCount = ageDictionary.Count; 38Console.WriteLine($"\nNumber of Entries: {entryCount}");
  • Defining a Dictionary: Use Dictionary<TKey, TValue> where TKey is the type for keys and TValue is the type for values.
  • Adding: Initial addition of entries, printed after insertion.
  • Updating: Demonstrates updating the existing entry by reassigning Alice's age.
  • Retrieving: Uses ContainsKey to check existence and retrieve values.
  • Removing: Uses the Remove method to delete entries.
  • Counting: Uses the Count property to get the number of entries.

Now that you're familiar with these operations, let's apply them in a PhoneBook class.

Implementing a PhoneBook with C# Dictionaries

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).

C#
1using System; 2using System.Collections.Generic; 3 4// Define the PhoneBook class 5class PhoneBook 6{ 7 private Dictionary<string, string> contacts; 8 9 public PhoneBook() 10 { 11 // Initialize the Dictionary 12 contacts = new Dictionary<string, string>(); 13 } 14 15 public void AddContact(string name, string phoneNumber) 16 { 17 // Add or update a contact 18 contacts[name] = phoneNumber; 19 } 20 21 public string GetPhoneNumber(string name) 22 { 23 // Retrieve a phone number or return "Not Found" 24 return contacts.ContainsKey(name) ? contacts[name] : "Not Found"; 25 } 26} 27 28// Define the Program class containing the Main method 29class Program 30{ 31 static void Main(string[] args) 32 { 33 // Create a PhoneBook instance 34 PhoneBook phoneBook = new PhoneBook(); 35 36 // Add contacts 37 phoneBook.AddContact("Alice", "123-456-7890"); 38 phoneBook.AddContact("Bob", "234-567-8901"); 39 40 // Get and display phone numbers 41 Console.WriteLine(phoneBook.GetPhoneNumber("Alice")); // Output: "123-456-7890" 42 Console.WriteLine(phoneBook.GetPhoneNumber("Bobby")); // Output: "Not Found" 43 } 44}

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

Operations in Dictionaries

C# Dictionaries enable a variety of operations for manipulating data, such as adding, retrieving, and deleting key-value pairs, and more. Understanding these operations is crucial for efficient data handling in C#.

To add or update entries in a Dictionary, you use index notation or the Add 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 Dictionary without needing a predefined structure.

The TryGetValue method retrieves the value associated with a specific key. It provides a safe way to access values, returning a boolean indicating success, and outputting the value if the key exists.

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

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

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

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

C#
1using System; 2using System.Collections.Generic; 3 4// Define TaskManager class 5class TaskManager 6{ 7 private Dictionary<string, string> tasks; 8 9 public TaskManager() 10 { 11 // Initialize with an empty Dictionary 12 tasks = new Dictionary<string, string>(); 13 } 14 15 public void AddUpdateTask(string taskName, string status) 16 { 17 // Add a new task or update an existing task 18 tasks[taskName] = status; 19 } 20 21 public string GetTaskStatus(string taskName) 22 { 23 // Use a nullable string type for status to address potential nullability 24 return tasks.TryGetValue(taskName, out string? status) ? status : "Not Found"; 25 } 26 27 public void DeleteTask(string taskName) 28 { 29 // Removes a task using its name 30 if (tasks.ContainsKey(taskName)) 31 { 32 tasks.Remove(taskName); 33 } 34 else 35 { 36 Console.WriteLine($"Task '{taskName}' not found."); 37 } 38 } 39 40 public int GetTaskCount() 41 { 42 // Returns the number of tasks in the TaskManager 43 return tasks.Count; 44 } 45} 46 47// Define Program class with Main method 48class Program 49{ 50 static void Main(string[] args) 51 { 52 // Create a TaskManager instance 53 TaskManager myTasks = new TaskManager(); 54 55 // Add tasks and update them 56 myTasks.AddUpdateTask("Buy Milk", "Pending"); 57 Console.WriteLine(myTasks.GetTaskStatus("Buy Milk")); // Output: Pending 58 59 myTasks.AddUpdateTask("Buy Milk", "Completed"); 60 Console.WriteLine(myTasks.GetTaskStatus("Buy Milk")); // Output: Completed 61 62 // Delete a task 63 myTasks.DeleteTask("Buy Milk"); 64 Console.WriteLine(myTasks.GetTaskStatus("Buy Milk")); // Output: Not Found 65 66 // Add another task and get the count 67 myTasks.AddUpdateTask("Clean House", "In Progress"); 68 Console.WriteLine(myTasks.GetTaskCount()); // Output: 1 69 } 70}

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

Looping Through Dictionaries

C# provides an elegant way to loop through Dictionaries using foreach loops. We can iterate through keys, values, or both simultaneously using specific constructs provided by the KeyValuePair<TKey, TValue> class.

  • Keys: To loop through all the keys in the Dictionary, you can use the Keys property.
  • Values: To loop through all the values in the Dictionary, you can use the Values property.
  • Entries: To loop through all key-value pairs in the Dictionary, you can use the foreach with KeyValuePair<TKey, TValue>.

Let's explore this in our Task Manager example:

C#
1using System; 2using System.Collections.Generic; 3 4class TaskManager { 5 private Dictionary<string, string> tasks; 6 7 public TaskManager() { 8 tasks = new Dictionary<string, string>(); 9 } 10 11 public void AddTask(string taskName, string status) { 12 tasks[taskName] = status; 13 } 14 15 public void PrintAllTaskKeys() { 16 foreach (string taskName in tasks.Keys) { 17 Console.WriteLine(taskName); 18 } 19 } 20 21 public void PrintAllTaskValues() { 22 foreach (string status in tasks.Values) { 23 Console.WriteLine(status); 24 } 25 } 26 27 public void PrintAllEntries() { 28 foreach (KeyValuePair<string, string> entry in tasks) { 29 Console.WriteLine($"{entry.Key}: {entry.Value}"); 30 } 31 } 32} 33 34class Program 35{ 36 static void Main(string[] args) 37 { 38 TaskManager myTasks = new TaskManager(); 39 myTasks.AddTask("Buy Milk", "Pending"); 40 myTasks.AddTask("Pay Bills", "Completed"); 41 42 myTasks.PrintAllTaskKeys(); 43 /* Output: 44 Buy Milk 45 Pay Bills 46 */ 47 48 myTasks.PrintAllTaskValues(); 49 /* Output: 50 Pending 51 Completed 52 */ 53 54 myTasks.PrintAllEntries(); 55 /* Output: 56 Buy Milk: Pending 57 Pay Bills: Completed 58 */ 59 } 60}

In this example, we use foreach loops to iterate over a dictionary's keys, values, and entries using the Keys, Values, and foreach constructs. This allows us to easily print all tasks in our task manager along with their statuses.

Nesting with Dictionaries

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

C#
1using System; 2using System.Collections.Generic; 3 4class StudentDatabase { 5 private Dictionary<string, Dictionary<string, string>> students; 6 7 public StudentDatabase() { 8 students = new Dictionary<string, Dictionary<string, string>>(); 9 } 10 11 public void AddStudent(string name, Dictionary<string, string> subjects) { 12 students[name] = subjects; 13 } 14 15 public string GetMark(string name, string subject) { 16 if (students.TryGetValue(name, out Dictionary<string, string>? subjects)) { 17 return subjects.ContainsKey(subject) ? subjects[subject] : "N/A"; 18 } 19 return "N/A"; 20 } 21 22 public void PrintDatabase() { 23 foreach (KeyValuePair<string, Dictionary<string, string>> student in students) { 24 Console.WriteLine("Student: " + student.Key); 25 foreach (KeyValuePair<string, string> subject in student.Value) { 26 Console.WriteLine($" Subject: {subject.Key}, Grade: {subject.Value}"); 27 } 28 } 29 } 30} 31 32class Program 33{ 34 static void Main(string[] args) 35 { 36 StudentDatabase studentDb = new StudentDatabase(); 37 studentDb.AddStudent("Alice", new Dictionary<string, string> { { "Math", "A" }, { "English", "B" } }); 38 39 Console.WriteLine(studentDb.GetMark("Alice", "English")); // Output: "B" 40 Console.WriteLine(studentDb.GetMark("Alice", "History")); // Output: "N/A" 41 studentDb.PrintDatabase(); 42 /* Output: 43 Student: Alice 44 Subject: Math, Grade: A 45 Subject: English, Grade: B 46 */ 47 } 48}
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 Dictionaries 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 C# Dictionary:

C#
1using System; 2using System.Collections.Generic; 3 4class ShoppingCart { 5 6 private Dictionary<string, int> cart; 7 8 public ShoppingCart() { 9 cart = new Dictionary<string, int>(); 10 } 11 12 public void AddProduct(string productName, int quantity) { 13 if (cart.ContainsKey(productName)) { 14 cart[productName] += quantity; 15 } else { 16 cart[productName] = quantity; 17 } 18 } 19 20 public void RemoveProduct(string productName) { 21 if (cart.ContainsKey(productName)) { 22 cart.Remove(productName); 23 } else { 24 Console.WriteLine($"{productName} not found in your cart."); 25 } 26 } 27 28 public void ShowCart() { 29 if (cart.Count == 0) { 30 Console.WriteLine("Your shopping cart is empty."); 31 } else { 32 foreach (KeyValuePair<string, int> entry in cart) { 33 Console.WriteLine($"{entry.Key}: {entry.Value}"); 34 } 35 } 36 } 37} 38 39class Program 40{ 41 static void Main(string[] args) 42 { 43 ShoppingCart myCart = new ShoppingCart(); 44 45 myCart.AddProduct("Apples", 5); 46 myCart.AddProduct("Bananas", 2); 47 myCart.AddProduct("Apples", 3); 48 49 myCart.ShowCart(); 50 /* Output: 51 Apples: 8 52 Bananas: 2 53 */ 54 55 myCart.RemoveProduct("Bananas"); 56 myCart.ShowCart(); 57 /* Output: 58 Apples: 8 59 */ 60 } 61}

This example showcases the practical application of Dictionaries 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 C# applications.

Lesson Summary and Practice

Well done! Today, we delved into C# Dictionaries and explored various operations on Dictionaries. We now invite you to get hands-on experience with the upcoming practice exercises. To master these concepts and hone your C# Dictionary 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.