在數據處理中,去除集合中的重複元素是一個常見的需求。.NET 6 和 .NET 7 引入了 DistinctBy 方法,這是一個非常實用的新特性,可以方便地根據指定的鍵對集合進行去重。
本文將詳細介紹 DistinctBy 方法的使用,並通過具體的案例來展示其在實際開發中的應用。
正文
1、DistinctBy 方法
DistinctBy 方法允許我們在 LINQ 查詢中根據某個鍵對集合中的元素進行去重。
這個方法返回一個新的集合,其中只包含根據指定鍵唯一確定的元素。
public static IEnumerable<TSource> DistinctBy<TSource, TKey>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector );
2、基本用法
最簡單的用法是在 LINQ 查詢中直接調用 DistinctBy 方法,然後處理去重後的集合。
説明
假設我們有一個用户列表,我們想要根據用户名去除重複的用户。
using System.Linq; class User { public string Name { get; set; } public int Age { get; set; } } var users = new List<User> { new User { Name = "Alice", Age = 25 }, new User { Name = "Bob", Age = 32 }, new User { Name = "Alice", Age = 28 }, new User { Name = "David", Age = 35 } }; var distinctUsers = users.DistinctBy(user => user.Name); foreach (var user in distinctUsers) { Console.WriteLine($"Name: {user.Name}, Age: {user.Age}"); }
輸出結果:
Name: Alice, Age: 25 Name: Bob, Age: 32 Name: David, Age: 35
過濾前後元素還是保持原有的順序,我們可以查看源碼。
源碼
private static IEnumerable<TSource> DistinctByIterator<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer) { using IEnumerator<TSource> enumerator = source.GetEnumerator(); if (enumerator.MoveNext()) { var set = new HashSet<TKey>(DefaultInternalSetCapacity, comparer); do { TSource element = enumerator.Current; if (set.Add(keySelector(element))) { yield return element; } } while (enumerator.MoveNext()); } }
通過查看源碼,可以看到是利用了 HashSet 去重,元素順序並未被打亂。
在處理集合時,我們經常需要去除重複的元素,同時保持原有的順序。
使用 HashSet 可以高效地實現這一目標。
首先將指定的鍵嘗試添加到 HashSet 中,如果添加成功,説明該鍵沒有重複;
如果添加失敗,説明已經存在相同的鍵,此元素將被過濾掉。
3、複雜用法
DistinctBy 方法可以用於更復雜的去重邏輯,例如根據多個屬性進行去重。
説明
假設我們有一個訂單列表,我們想要根據客户名稱和訂單金額去除重複的訂單。
class Order { public int OrderId { get; set; } public string CustomerName { get; set; } public decimal Amount { get; set; } } var orders = new List<Order> { new Order { OrderId = 1, CustomerName = "Alice", Amount = 100.0m }, new Order { OrderId = 2, CustomerName = "Bob", Amount = 150.0m }, new Order { OrderId = 3, CustomerName = "Alice", Amount = 100.0m }, new Order { OrderId = 4, CustomerName = "Charlie", Amount = 120.0m }, new Order { OrderId = 5, CustomerName = "Bob", Amount = 150.0m } }; var distinctOrders = orders.DistinctBy(order => (order.CustomerName, order.Amount)); foreach (var order in distinctOrders) { Console.WriteLine($"Order ID: {order.OrderId}, Customer: {order.CustomerName}, Amount: {order.Amount}"); }
輸出結果:
Order ID: 1, Customer: Alice, Amount: 100.0 Order ID: 2, Customer: Bob, Amount: 150.0 Order ID: 4, Customer: Charlie, Amount: 120.0
4、性能考慮
DistinctBy 方法在內部使用哈希表來跟蹤已經出現的鍵,因此在大多數情況下性能非常好。但在處理非常大的數據集時,仍然需要注意內存使用情況。
説明
假設我們有一個包含數百萬條記錄的大集合,我們需要根據某個鍵進行去重。
var largeCollection = Enumerable.Range(1, 10000000).Select(i => new { Id = i, Value = i % 1000 }); var distinctLargeCollection = largeCollection.DistinctBy(item => item.Value); Console.WriteLine($"Distinct count: {distinctLargeCollection.Count()}");
5、異步 LINQ 查詢中的使用
DistinctBy 方法也可以在異步 LINQ 查詢中使用,結合 IAsyncEnumerable<T> 類型,處理大量數據時更加高效。
説明
假設我們有一個異步方法返回一個用户列表,我們想要根據用户名去除重複的用户。
using System.Net.Http.Json; public async IAsyncEnumerable<User> GetUsersAsync() { var response = await httpClient.GetAsync("https://api.example.com/users"); var usersJson = await response.Content.ReadAsStringAsync(); // 使用Json序列化工具解析用户列表 var users = JsonSerializer.Deserialize<List<User>>(usersJson); foreach (var user in users) { yield return user; } } // 使用異步LINQ查詢 var distinctUsers = await GetUsersAsync().DistinctByAsync(user => user.Name).ToListAsync(); foreach (var user in distinctUsers) { Console.WriteLine($"Name: {user.Name}, Age: {user.Age}"); }
總結
DistinctBy 方法是 .NET 6 和 .NET 7 中 LINQ 的一個非常實用的新特性。我們在 LINQ 查詢中根據指定的鍵對集合進行去重,簡化了代碼並提高了開發效率。
希望本文能幫助大家更好地理解和利用 .NET 6 和 .NET 7 中 LINQ 的 DistinctBy
最後