Welcome to our lesson on mastering data aggregation and data streams in C#. In this lesson, we'll start by building a basic sales records aggregator. Then, we'll extend its functionality to handle more complex operations such as filtering, data aggregation, and formatted outputs. By the end of this session, you'll be able to manage and format data streams efficiently using C#.
To begin, we'll implement a basic sales record aggregator. Here are the methods we'll be focusing on:
addSale(int saleId, double amount)
- Adds a sale record with a unique identifier saleId
and an amount
. If a sale with the same saleId
already exists, it updates the amount.getSale(int saleId)
- Retrieves the sale amount associated with the saleId
. If the sale does not exist, it returns null
.deleteSale(int saleId)
- Deletes the sale record with the given saleId
. Returns true
if the sale was deleted and false
if the sale does not exist.Are these methods clear so far? Great! Let's now look at how we would implement them.
Here is the complete code for the starter task:
C#1using System; 2using System.Collections.Generic; 3 4public class SalesAggregator 5{ 6 private Dictionary<int, double> sales; 7 8 public SalesAggregator() 9 { 10 sales = new Dictionary<int, double>(); // Initialize an empty dictionary to store sales records. 11 } 12 13 public void addSale(int saleId, double amount) 14 { 15 sales[saleId] = amount; // Add or update the sale with the provided saleId and amount. 16 } 17 18 public double? getSale(int saleId) 19 { 20 // Retrieve and return the sale amount for the given saleId, or null if it doesn't exist. 21 return sales.ContainsKey(saleId) ? sales[saleId] : (double?)null; 22 } 23 24 public bool deleteSale(int saleId) 25 { 26 return sales.Remove(saleId); // Remove the sale record and return true if it existed, false otherwise. 27 } 28} 29 30public class Program 31{ 32 public static void Main() 33 { 34 SalesAggregator aggregator = new SalesAggregator(); 35 36 // Add sales 37 aggregator.addSale(1, 100.50); 38 aggregator.addSale(2, 200.75); 39 40 // Get sale 41 Console.WriteLine(aggregator.getSale(1)); // Output: 100.5 42 43 // Delete sale 44 Console.WriteLine(aggregator.deleteSale(2)); // Output: True 45 Console.WriteLine(aggregator.getSale(2)); // Output: null 46 } 47}
Explanation:
SalesAggregator
class initializes an empty dictionary to store sales records.addSale
method adds a new sale or updates the amount for an existing sale ID.getSale
method retrieves the amount for a given sale ID or returns null
if the sale does not exist.deleteSale
method removes the sale record for the given sale ID or returns false
if the sale does not exist.Now that we have our basic aggregator, let's extend it to include more advanced functionalities.
To increase the complexity and usefulness of our sales aggregator, we'll adjust some existing methods and introduce some new methods and functionalities involving formatted outputs.
addSale(int saleId, double amount, DateTime date)
- Adds or updates a sale record with a unique identifier saleId
, amount
, and a date
.aggregateSales(double minAmount = 0)
- Returns an object with the total number of sales and the total amount of sales where the sale amount is above minAmount
. The object format looks like this:
C#1public class SalesStatistics 2{ 3 public int totalSales { get; set; } 4 public double totalAmount { get; set; } 5}
formatSales(double minAmount = 0)
- Returns the sales data, filtered by minAmount
, formatted as JSON. Includes aggregated sales statistics in the output.getSalesInDateRange(DateTime startDate, DateTime endDate)
- Retrieves all sales that occurred within the given date range, inclusive. Each sale includes saleId
, amount
, and date
.Let's implement these methods step-by-step.
We'll first modify the addSale
method to accept a date.
C#1public void addSale(int saleId, double amount, DateTime date) 2{ 3 // Add or update the sale with the provided saleId, amount, and date. 4 sales[saleId] = new SaleRecord { amount = amount, date = date }; 5} 6 7private class SaleRecord 8{ 9 public double amount { get; set; } 10 public DateTime date { get; set; } 11}
This ensures that each sale record includes a date in addition to the amount.
Now, we create the aggregateSales
method:
C#1public SalesStatistics aggregateSales(double minAmount = 0) 2{ 3 int totalSales = 0; 4 double totalAmount = 0.0; 5 foreach (var sale in sales.Values) 6 { 7 if (sale.amount > minAmount) 8 { 9 totalSales += 1; // Increment the total sales count. 10 totalAmount += sale.amount; // Add to the total amount. 11 } 12 } 13 return new SalesStatistics { totalSales = totalSales, totalAmount = totalAmount }; // Return the aggregated results. 14} 15 16// Define SalesStatistics class 17public class SalesStatistics 18{ 19 public int totalSales { get; set; } 20 public double totalAmount { get; set; } 21} 22 23public class Program 24{ 25 public static void Main() 26 { 27 SalesAggregator aggregator = new SalesAggregator(); 28 29 // Add sales with date 30 aggregator.addSale(1, 100.50, new DateTime(2023, 1, 1)); 31 aggregator.addSale(2, 200.75, new DateTime(2023, 1, 15)); 32 33 // Aggregate sales 34 var stats = aggregator.aggregateSales(50); 35 Console.WriteLine($"Total Sales: {stats.totalSales}, Total Amount: {stats.totalAmount}"); 36 // Output: Total Sales: 2, Total Amount: 301.25 37 } 38}
This method iterates through the sales and sums up those that exceed the minAmount
.
Next, we'll create the formatSales
method to output data in JSON format using System.Text.Json
.
C#1using System.Text.Json; 2 3public string formatSales(double minAmount = 0) 4{ 5 // Filter and format sales data based on minAmount. 6 var filteredSales = new List<object>(); 7 foreach (var sale in sales) 8 { 9 if (sale.Value.amount > minAmount) 10 { 11 filteredSales.Add(new { SaleId = sale.Key, sale.Value.amount, sale.Value.date }); 12 } 13 } 14 15 // Aggregate sales statistics. 16 var statistics = aggregateSales(minAmount); 17 18 // Combine sales data and statistics into the final result. 19 var result = new 20 { 21 Sales = filteredSales, 22 Statistics = statistics 23 }; 24 25 return JsonSerializer.Serialize(result); // Convert the result to JSON. 26} 27 28public class Program 29{ 30 public static void Main() 31 { 32 SalesAggregator aggregator = new SalesAggregator(); 33 34 // Add sales with date 35 aggregator.addSale(1, 100.50, new DateTime(2023, 1, 1)); 36 aggregator.addSale(2, 200.75, new DateTime(2023, 1, 15)); 37 38 // Format sales to JSON 39 var formattedSales = aggregator.formatSales(50); 40 Console.WriteLine(formattedSales); 41 // Output: {"Sales":[{"SaleId":1,"Amount":100.5,"Date":"2023-01-01T00:00:00"},{"SaleId":2,"Amount":200.75,"Date":"2023-01-15T00:00:00"}],"Statistics":{"TotalSales":2,"TotalAmount":301.25}} 42 } 43}
This function formats the sales data as JSON and includes aggregated statistics.
Finally, let's implement the getSalesInDateRange
method:
C#1public List<object> getSalesInDateRange(DateTime startDate, DateTime endDate) 2{ 3 var result = new List<object>(); 4 foreach (var sale in sales) 5 { 6 if (sale.Value.date >= startDate && sale.Value.date <= endDate) 7 { 8 result.Add(new { SaleId = sale.Key, sale.Value.amount, sale.Value.date }); 9 } 10 } 11 return result; 12} 13 14public class Program 15{ 16 public static void Main() 17 { 18 SalesAggregator aggregator = new SalesAggregator(); 19 20 // Add sales with date 21 aggregator.addSale(1, 100.50, new DateTime(2023, 1, 1)); 22 aggregator.addSale(2, 200.75, new DateTime(2023, 1, 15)); 23 24 // Get sales in date range 25 var salesInRange = aggregator.getSalesInDateRange(new DateTime(2023, 1, 1), new DateTime(2023, 12, 31)); 26 foreach (var sale in salesInRange) 27 { 28 Console.WriteLine($"{sale}"); 29 } 30 // Output: { SaleId = 1, Amount = 100.5, Date = new DateTime(2023, 1, 1) } 31 // { SaleId = 2, Amount = 200.75, Date = new DateTime(2023, 1, 15) } 32 } 33}
This method retrieves all sales within the specified date range.
Congratulations! You've now extended a basic sales aggregator to an advanced one capable of filtering, aggregating, and providing formatted outputs in C#. These skills are crucial for handling data streams efficiently, especially when dealing with large datasets. Feel free to experiment with similar challenges to reinforce your understanding. Well done, and see you in the next lesson!