如何对已排序的C# List<Order>按连续相同的DeliveryPerson进行分组
实现连续相同DeliveryPerson的Order分组
这个需求用C#标准库的GroupBy是搞不定的——因为它会把所有DeliveryPerson相同的订单都归到同一组,完全不管它们在原列表里是不是连续的。下面给你两种实用的实现方式,都是基于IEnumerable的,兼顾效率和灵活性:
方法一:自定义扩展方法(推荐)
写一个通用的扩展方法,以后任何需要连续分组的场景都能复用。这个方法用懒加载的IEnumerable实现,不用一次性把所有数据都加载到内存,性能更优:
public static class EnumerableExtensions { public static IEnumerable<IEnumerable<T>> GroupConsecutiveBy<T, TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector) where TKey : IEquatable<TKey> { if (source == null) throw new ArgumentNullException(nameof(source)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); using var enumerator = source.GetEnumerator(); if (!enumerator.MoveNext()) yield break; var currentKey = keySelector(enumerator.Current); var currentGroup = new List<T> { enumerator.Current }; while (enumerator.MoveNext()) { var item = enumerator.Current; var key = keySelector(item); if (currentKey.Equals(key)) { currentGroup.Add(item); } else { yield return currentGroup; currentKey = key; currentGroup = new List<T> { item }; } } // 别忘了返回最后一组 yield return currentGroup; } }
使用示例
假设你已经有了排序好的List<Order> sortedOrders,直接调用这个扩展方法就行:
// 按连续的DeliveryPerson分组 var consecutiveGroups = sortedOrders.GroupConsecutiveBy(order => order.DeliveryPerson); // 遍历分组输出结果 foreach (var group in consecutiveGroups) { var firstOrder = group.First(); var orderIds = string.Join(", ", group.Select(o => $"Order{o.Id}")); Console.WriteLine($"Group: {orderIds} [DeliveryPerson: {firstOrder.DeliveryPerson}]"); }
输出结果会和你预期的一致:
Group: Order12 [DeliveryPerson: Andrea]
Group: Order13 [DeliveryPerson: David]
Group: Order14, Order15 [DeliveryPerson: Andrea]
Group: Order16 [DeliveryPerson: Linda]
方法二:手动循环分组(适合快速理解)
如果不想写扩展方法,也可以直接用循环手动构建分组,逻辑更直观,适合新手理解:
List<List<Order>> consecutiveGroups = new List<List<Order>>(); foreach (var order in sortedOrders) { // 如果分组列表为空,或者最后一组的DeliveryPerson和当前订单不同,就新建分组 if (consecutiveGroups.Count == 0 || consecutiveGroups.Last().First().DeliveryPerson != order.DeliveryPerson) { consecutiveGroups.Add(new List<Order> { order }); } else { // 否则加入最后一个分组 consecutiveGroups.Last().Add(order); } }
关于数据类型的选择
- 如果只需要遍历处理分组数据,优先用
IEnumerable<IEnumerable<Order>>(方法一的返回类型),因为它是懒加载的,内存占用更低,尤其是处理大列表的时候。 - 如果需要修改分组后的集合(比如添加/删除元素),就用
List<List<Order>>(方法二的返回类型),它提供了完整的集合操作方法。
内容的提问来源于stack exchange,提问作者Adrian
相关产品推荐
相关产品推荐

