Welcome to today's lesson on applying data filtering and aggregation in a real-world scenario using a user management system. We'll start by building a foundational structure that can handle basic user operations. Then, we'll expand it by introducing more advanced functionalities that allow filtering and aggregating user data.
In our starter task, we will implement a class that manages basic operations on a collection of user data, specifically handling adding new users, retrieving user profiles, and updating user profiles.
Here are the starter task methods:
addUser(string userId, int age, string country, bool subscribed)
- adds a new user with the specified attributes. Returns true
if the user was added successfully and false
if a user with the same userId
already exists.getUser(string userId)
- returns the user's profile as an object if the user exists; otherwise, returns null
.updateUser(string userId, int? age, string country, bool? subscribed)
- updates the user's profile based on non-null
parameters. Returns true
if the user exists and was updated, false
otherwise.The C# implementation of our starter task is shown below:
C#1using System; 2using System.Collections.Generic; 3 4public class UserManager 5{ 6 private Dictionary<string, UserProfile> users; 7 8 public UserManager() 9 { 10 users = new Dictionary<string, UserProfile>(); 11 } 12 13 public bool addUser(string userId, int age, string country, bool subscribed) 14 { 15 if (users.ContainsKey(userId)) 16 { 17 return false; 18 } 19 users[userId] = new UserProfile { Age = age, Country = country, Subscribed = subscribed }; 20 return true; 21 } 22 23 public UserProfile getUser(string userId) 24 { 25 if (users.TryGetValue(userId, out var profile)) 26 { 27 return profile; 28 } 29 return null; 30 } 31 32 public bool updateUser(string userId, int? age, string country, bool? subscribed) 33 { 34 if (!users.ContainsKey(userId)) 35 { 36 return false; 37 } 38 if (age.HasValue) 39 { 40 users[userId].Age = age.Value; 41 } 42 if (country != null) 43 { 44 users[userId].Country = country; 45 } 46 if (subscribed.HasValue) 47 { 48 users[userId].Subscribed = subscribed.Value; 49 } 50 return true; 51 } 52} 53 54public class UserProfile 55{ 56 public int Age { get; set; } 57 public string Country { get; set; } 58 public bool Subscribed { get; set; } 59} 60 61class Program 62{ 63 static void Main() 64 { 65 var um = new UserManager(); 66 Console.WriteLine(um.addUser("u1", 25, "USA", true)); // true 67 Console.WriteLine(um.addUser("u2", 30, "Canada", false)); // true 68 Console.WriteLine(um.addUser("u1", 22, "Mexico", true)); // false 69 Console.WriteLine(um.getUser("u1")); // UserProfile with specified attributes 70 Console.WriteLine(um.updateUser("u1", 26, null, null)); // true 71 Console.WriteLine(um.updateUser("u3", 19, "UK", false)); // false 72 } 73}
This implementation covers all our starter methods. Let's move forward and introduce more complex functionalities.
With our foundational structure in place, it's time to add functionalities for filtering user data and aggregating statistics.
Here are new methods to implement:
filterUsers(int? minAge, int? maxAge, string country, bool? subscribed)
:
null
, meaning that the criterion should not be applied during filtering.aggregateStats()
- returns statistics in the form of an object:
totalUsers
: Total number of usersaverageAge
: Average age of all users (rounded down to the nearest integer)subscribedRatio
: Ratio of subscribed users to total users (as a float with two decimals)This method filters users based on the criteria provided. Let's see how it works:
C#1using System; 2using System.Collections.Generic; 3 4public class UserManager 5{ 6 // Existing methods... 7 8 // Method to filter users based on criteria 9 public List<string> filterUsers(int? minAge, int? maxAge, string country, bool? subscribed) 10 { 11 var filteredUsers = new List<string>(); 12 13 foreach (var kvp in users) 14 { 15 var userId = kvp.Key; 16 var profile = kvp.Value; 17 18 // Check minimum age criterion 19 if (minAge.HasValue && profile.Age < minAge) 20 { 21 continue; 22 } 23 // Check maximum age criterion 24 if (maxAge.HasValue && profile.Age > maxAge) 25 { 26 continue; 27 } 28 // Check country criterion 29 if (country != null && profile.Country != country) 30 { 31 continue; 32 } 33 // Check subscription status criterion 34 if (subscribed.HasValue && profile.Subscribed != subscribed) 35 { 36 continue; 37 } 38 filteredUsers.Add(userId); 39 } 40 return filteredUsers; 41 } 42} 43 44class Program 45{ 46 static void Main() 47 { 48 var um = new UserManager(); 49 Console.WriteLine(um.addUser("u1", 25, "USA", true)); // true 50 Console.WriteLine(um.addUser("u2", 30, "Canada", false)); // true 51 Console.WriteLine(um.addUser("u1", 22, "Mexico", true)); // false 52 Console.WriteLine(um.getUser("u1")); // UserProfile with specified attributes 53 Console.WriteLine(um.updateUser("u1", 26, null, null)); // true 54 Console.WriteLine(um.updateUser("u3", 19, "UK", false)); // false 55 56 Console.WriteLine(string.Join(", ", um.filterUsers(20, 30, "USA", true))); // ["u1"] 57 } 58}
filterUsers
method filters users based on minAge
, maxAge
, country
, and subscribed
status criteria.users
dictionary and checks each user's profile against the provided criteria.filteredUsers
list, which is then returned.This method aggregates statistics from the user profiles. Let's implement it:
C#1using System; 2using System.Collections.Generic; 3 4public class UserManager 5{ 6 // Existing methods... 7 8 // Method to aggregate statistics from user profiles 9 public Stats aggregateStats() 10 { 11 var totalUsers = users.Count; 12 if (totalUsers == 0) 13 { 14 return new Stats { TotalUsers = 0, AverageAge = 0, SubscribedRatio = 0.00 }; 15 } 16 17 // Calculate total age by summing ages of all users 18 var totalAge = 0; 19 var subscribedUsers = 0; 20 foreach (var profile in users.Values) 21 { 22 totalAge += profile.Age; 23 if (profile.Subscribed) 24 { 25 subscribedUsers++; 26 } 27 } 28 29 // Calculate average age (rounded down) 30 var averageAge = totalAge / totalUsers; 31 // Calculate subscribed ratio (to two decimals) 32 var subscribedRatio = Math.Round((double)subscribedUsers / totalUsers, 2); 33 34 return new Stats { TotalUsers = totalUsers, AverageAge = averageAge, SubscribedRatio = subscribedRatio }; 35 } 36} 37 38public class Stats 39{ 40 public int TotalUsers { get; set; } 41 public int AverageAge { get; set; } 42 public double SubscribedRatio { get; set; } 43} 44 45class Program 46{ 47 static void Main() 48 { 49 var um = new UserManager(); 50 Console.WriteLine(um.addUser("u1", 25, "USA", true)); // true 51 Console.WriteLine(um.addUser("u2", 30, "Canada", false)); // true 52 Console.WriteLine(um.addUser("u1", 22, "Mexico", true)); // false 53 Console.WriteLine(um.getUser("u1")); // UserProfile with specified attributes 54 Console.WriteLine(um.updateUser("u1", 26, null, null)); // true 55 Console.WriteLine(um.updateUser("u3", 19, "UK", false)); // false 56 57 Console.WriteLine(string.Join(", ", um.filterUsers(20, 30, "USA", true))); // ["u1"] 58 Console.WriteLine(um.aggregateStats().TotalUsers); // 2 59 Console.WriteLine(um.aggregateStats().AverageAge); // 27 60 Console.WriteLine(um.aggregateStats().SubscribedRatio); // 0.50 61 } 62}
This aggregateStats
method calculates and returns aggregate statistics about the users in the form of a Stats class object. It first determines TotalUsers
, the total number of users. If there are no users, it returns an object with zeroed statistics. Otherwise, it calculates TotalAge
by summing the ages of all users and counts SubscribedUsers
who are subscribed. It then computes AverageAge
by performing integer division of TotalAge
by TotalUsers
and calculates SubscribedRatio
by dividing SubscribedUsers
by TotalUsers
and rounding to two decimals. The resulting statistics object includes TotalUsers
, AverageAge
, and SubscribedRatio
.
Great job! Today, you've learned how to effectively handle user data by implementing advanced functionalities like filtering and aggregation on top of a basic system. This is a critical skill in real-life software development, where you often need to extend existing systems to meet new requirements.
I encourage you to practice solving similar challenges to solidify your understanding of data filtering and aggregation. Happy coding, and see you in the next lesson!