如何将嵌套foreach循环代码改写为LINQ语句?
Hey there! No worries at all—LINQ can feel a bit overwhelming when you're just starting out, but once you get the hang of its patterns, it makes code much cleaner and more concise. Let's break down how to convert your nested loops into LINQ, and even fix a potential issue in the original code along the way.
First, let's recap what your original code does (and note a possible bug):
- For each employee, create an
Earningsobject - Add the new
EarningstoEarningsListonly if it doesn't already exist - Loop through all products to calculate total earnings for the employee and add it to the newly created
Earningsobject (this is the bug: if an entry already exists inEarningsList, that existing entry won't get the updated earnings—you're only adding money to a discarded new instance)
I'll assume you want to fix that bug by updating existing Earnings entries if they exist. Here's how to do that with LINQ:
Option 1: Efficient LINQ + Foreach (Balanced Readability)
This version uses LINQ to pre-calculate total earnings per employee first, then uses a foreach to update or create entries in EarningsList:
// Step 1: Precompute total earnings for each employee by grouping products var employeeTotalEarnings = ProductList .SelectMany(dayData => dayData.Products) // Flatten all products from all DaysData into one list .GroupBy(product => new { product.EmployeeName, product.EmployeeLastName }) // Group products by employee's full name .ToDictionary( group => group.Key, group => group.Sum(p => p.Count * p.Price) // Calculate total earnings for each employee ); // Step 2: Update or create Earnings entries in EarningsList foreach (var employee in EmployeeList) { // Check if an Earnings entry already exists for this employee var existingEarnings = EarningsList.FirstOrDefault(e => e.Name == employee.Name && e.LastName == employee.LastName); Earnings earnings; if (existingEarnings == null) { // Create new entry if none exists earnings = new Earnings(employee.Name, employee.LastName, employee.Bank, employee.Account); EarningsList.Add(earnings); } else { // Use existing entry to update earnings earnings = existingEarnings; } // Add the precomputed total earnings if the employee has any products if (employeeTotalEarnings.TryGetValue( new { employee.Name, employee.LastName }, out double total)) { earnings.AddMoney(total); } }
Option 2: Full LINQ (More Concise)
If you prefer a more LINQ-focused approach, this version creates all necessary Earnings objects (with totals) and adds only the new ones to the list. Note: This doesn't update existing entries—if you need to update existing ones, stick with Option 1.
var newEarningsEntries = EmployeeList .Select(employee => { // Calculate total earnings for the employee var total = ProductList .SelectMany(dayData => dayData.Products) .Where(p => p.EmployeeName == employee.Name && p.EmployeeLastName == employee.LastName) .Sum(p => p.Count * p.Price); // Create Earnings object with the total var earnings = new Earnings(employee.Name, employee.LastName, employee.Bank, employee.Account); earnings.AddMoney(total); return earnings; }) .Where(newEntry => !EarningsList.Contains(newEntry)); // Filter out entries that already exist // Add all new entries to the list EarningsList.AddRange(newEarningsEntries);
Key Notes to Keep in Mind
- Equality Check: For
EarningsList.Contains(newEntry)to work correctly, yourEarningsclass needs to override theEquals()andGetHashCode()methods. By default, C# checks for reference equality (only the exact same object instance counts as equal), so you'll want to define equality based on properties likeName,LastName,Bank, andAccount. - Performance: Option 1 is more efficient because it groups products once upfront, instead of looping through all products for each employee (which the original code does).
- Readability: Don't force yourself to use pure LINQ if the foreach version is clearer. Mixing LINQ for querying/summing with a foreach for side effects (like updating a list) is often the most readable approach.
Let me know if you need help with any part of this, like implementing the equality methods for Earnings!
内容的提问来源于stack exchange,提问作者noob156

