Lesson 4
Managing Records with Nested Dictionaries and Lists in C#
Managing Records with Nested Dictionaries and Lists in C#

Welcome! Today, we are going to explore an engaging task that involves managing employee records within a company. Specifically, we will work with nested dictionaries and lists to add projects and tasks for employees and to retrieve those tasks as needed. This exercise will help you understand how to manipulate hierarchical data structures efficiently using C#.

Introducing Methods to Implement

Let's start by discussing the methods we will implement in our EmployeeRecords class.

  • AddProject(string employeeId, string projectName) - This method adds a new project to an employee's list of projects. If the project already exists for that employee, the method returns false. Otherwise, it adds the project and returns true.
  • AddTask(string employeeId, string projectName, string task) - This method adds a new task to a specified project for an employee. If the project does not exist for that employee, the method returns false. If the task is added successfully, it returns true.
  • GetTasks(string employeeId, string projectName) - This method retrieves all tasks for a specified project of an employee. If the project does not exist for that employee, the method returns null. Otherwise, it returns the list of tasks.
  • _Traverse(string employeeId, string projectName) - This private method helps to locate the nested structure for a given employee and project. If the path is valid and exists, it returns the target object. If it is invalid or does not exist, it returns null.
Step 1: Basic Class Structure

Now, let's build our EmployeeRecords class step by step, ensuring we understand each component clearly.

We'll start with the basic structure of the class and initialize our data storage.

C#
1using System; 2using System.Collections.Generic; 3 4public class EmployeeRecords 5{ 6 private Dictionary<string, Dictionary<string, List<string>>> records; 7 8 public EmployeeRecords() 9 { 10 records = new Dictionary<string, Dictionary<string, List<string>>>(); 11 } 12} 13 14public class Program 15{ 16 public static void Main() 17 { 18 var records = new EmployeeRecords(); 19 Console.WriteLine(records); 20 } 21}

In this initial setup, we define the EmployeeRecords class and create an instance variable records that is a nested dictionary. This structure will be used to store employee records, where each key is an employee ID and each value is another dictionary holding projects and their respective tasks.

Step 2: Implementing the Traverse Method

The _Traverse method helps to locate the nested structure for a given employee and project. If the path is valid and exists, it returns the target object. If the path is invalid or does not exist, it returns null.

Here is the breakdown of how _Traverse works:

C#
1using System; 2using System.Collections.Generic; 3 4public class EmployeeRecords 5{ 6 private Dictionary<string, Dictionary<string, List<string>>> records; 7 8 public EmployeeRecords() 9 { 10 records = new Dictionary<string, Dictionary<string, List<string>>>(); 11 } 12 13 private List<string>? _Traverse(string employeeId, string projectName) 14 { 15 if (!records.ContainsKey(employeeId)) 16 { 17 return null; 18 } 19 var employeeProjects = records[employeeId]; 20 if (projectName != null && !employeeProjects.ContainsKey(projectName)) 21 { 22 return null; 23 } 24 return projectName != null ? employeeProjects[projectName]! : null; 25 } 26} 27 28public class Program 29{ 30 public static void Main() 31 { 32 var records = new EmployeeRecords(); 33 Console.WriteLine(records); 34 } 35}

This method checks if the employeeId exists in the records dictionary and optionally checks for a projectName. If either is invalid, it returns null.

Step 3: Implementing AddProject Method

We'll implement the AddProject method and provide all necessary components for it to be executable.

C#
1using System; 2using System.Collections.Generic; 3 4public class EmployeeRecords 5{ 6 private Dictionary<string, Dictionary<string, List<string>>> records; 7 8 public EmployeeRecords() 9 { 10 records = new Dictionary<string, Dictionary<string, List<string>>>(); 11 } 12 13 public bool AddProject(string employeeId, string projectName) 14 { 15 if (!records.ContainsKey(employeeId)) 16 { 17 records[employeeId] = new Dictionary<string, List<string>>(); 18 } 19 if (records[employeeId].ContainsKey(projectName)) 20 { 21 return false; 22 } 23 else 24 { 25 records[employeeId][projectName] = new List<string>(); 26 return true; 27 } 28 } 29} 30 31public class Program 32{ 33 public static void Main() 34 { 35 var records = new EmployeeRecords(); 36 Console.WriteLine(records.AddProject("E123", "ProjectA")); // Returns True 37 Console.WriteLine(records.AddProject("E123", "ProjectA")); // Returns False 38 } 39}

In this implementation, the AddProject method ensures that each employee can have unique projects. It checks if the specified projectName already exists for the given employeeId within the records dictionary. If the project does not exist, it initializes a new entry with an empty list for tasks and returns True. This method highlights basic dictionary operations used to manage nested data effectively.

Step 4: Implementing AddTask Method

We'll next implement the AddTask method, along with all components needed for it to be executable.

C#
1using System; 2using System.Collections.Generic; 3 4public class EmployeeRecords 5{ 6 private Dictionary<string, Dictionary<string, List<string>>> records; 7 8 public EmployeeRecords() 9 { 10 records = new Dictionary<string, Dictionary<string, List<string>>>(); 11 } 12 13 private List<string>? _Traverse(string employeeId, string projectName) 14 { 15 if (!records.ContainsKey(employeeId)) 16 { 17 return null; 18 } 19 var employeeProjects = records[employeeId]; 20 if (projectName != null && !employeeProjects.ContainsKey(projectName)) 21 { 22 return null; 23 } 24 return projectName != null ? employeeProjects[projectName]! : null; 25 } 26 27 public bool AddProject(string employeeId, string projectName) 28 { 29 if (!records.ContainsKey(employeeId)) 30 { 31 records[employeeId] = new Dictionary<string, List<string>>(); 32 } 33 if (records[employeeId].ContainsKey(projectName)) 34 { 35 return false; 36 } 37 else 38 { 39 records[employeeId][projectName] = new List<string>(); 40 return true; 41 } 42 } 43 44 public bool AddTask(string employeeId, string projectName, string task) 45 { 46 var project = _Traverse(employeeId, projectName); 47 if (project == null) 48 { 49 return false; 50 } 51 project.Add(task); 52 return true; 53 } 54 55 public List<string>? GetTasks(string employeeId, string projectName) 56 { 57 return _Traverse(employeeId, projectName); 58 } 59} 60 61public class Program 62{ 63 public static void Main() 64 { 65 var records = new EmployeeRecords(); 66 records.AddProject("E123", "ProjectA"); 67 Console.WriteLine(records.AddTask("E123", "ProjectA", "Task1")); // Returns True 68 Console.WriteLine(records.AddTask("E123", "NonExistentProject", "Task3")); // Returns False 69 } 70}

In this step, the AddTask method is introduced, which adds a task to a specified project for an employee. By utilizing the _Traverse helper method, it checks if the specified project exists under the given employee ID. If the project is present, it adds the task to the project’s task list and returns true. If the project does not exist, it returns false. This structure demonstrates the efficient traversal and update of nested data.

Step 5: Implementing GetTasks Method

Lastly, we'll implement the GetTasks method, along with all necessary components for it to be executable.

C#
1using System; 2using System.Collections.Generic; 3 4public class EmployeeRecords 5{ 6 private Dictionary<string, Dictionary<string, List<string>>> records; 7 8 public EmployeeRecords() 9 { 10 records = new Dictionary<string, Dictionary<string, List<string>>>(); 11 } 12 13 private List<string>? _Traverse(string employeeId, string projectName) 14 { 15 if (!records.ContainsKey(employeeId)) 16 { 17 return null; 18 } 19 var employeeProjects = records[employeeId]; 20 if (projectName != null && !employeeProjects.ContainsKey(projectName)) 21 { 22 return null; 23 } 24 return projectName != null ? employeeProjects[projectName]! : null; 25 } 26 27 public bool AddProject(string employeeId, string projectName) 28 { 29 if (!records.ContainsKey(employeeId)) 30 { 31 records[employeeId] = new Dictionary<string, List<string>>(); 32 } 33 if (records[employeeId].ContainsKey(projectName)) 34 { 35 return false; 36 } 37 else 38 { 39 records[employeeId][projectName] = new List<string>(); 40 return true; 41 } 42 } 43 44 public bool AddTask(string employeeId, string projectName, string task) 45 { 46 var project = _Traverse(employeeId, projectName); 47 if (project == null) 48 { 49 return false; 50 } 51 project.Add(task); 52 return true; 53 } 54 55 public List<string>? GetTasks(string employeeId, string projectName) 56 { 57 return _Traverse(employeeId, projectName); 58 } 59} 60 61public class Program 62{ 63 public static void Main() 64 { 65 var records = new EmployeeRecords(); 66 records.AddProject("E123", "ProjectA"); 67 records.AddTask("E123", "ProjectA", "Task1"); 68 var tasks = records.GetTasks("E123", "ProjectA"); // Returns List<string> with "Task1" 69 if (tasks != null) 70 { 71 Console.WriteLine(string.Join(", ", tasks)); // Outputs: Task1 72 } 73 var nonExistentTasks = records.GetTasks("E123", "NonExistentProject"); 74 Console.WriteLine(nonExistentTasks == null); // Prints True 75 } 76}

By leveraging the _Traverse method, the GetTasks method efficiently locates the target project within the nested dictionary structure. If the project exists, it returns the list of tasks, otherwise, it returns null. This method exemplifies effective retrieval from hierarchical data structures, allowing easy access to manage employee tasks.

Lesson Summary

In this lesson, we successfully implemented the EmployeeRecords class for managing projects and tasks for employees using nested dictionaries and lists in C#. We covered methods for adding projects, adding tasks to those projects, and retrieving tasks from those projects, leveraging the _Traverse method to handle nested data efficiently.

Understanding how to work with hierarchical data structures allows you to efficiently manage complex data hierarchies, which improves your programming skills and problem-solving abilities.

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