如何在C#中对包含内部列表的对象列表按内部列表执行GroupBy分组
问题原因
GroupBy默认使用引用类型的相等比较逻辑,List<xyz>属于引用类型,默认仅对比内存地址,不会校验内部元素是否一致。因此哪怕两个courselist存储的课程完全相同,只要是不同的List实例,就会被判定为不同的分组键,无法分到同一组。
解决方案
方案1:自定义相等比较器(规范实现,性能更好)
首先实现针对List<xyz>的相等比较器:
public class CourseListComparer : IEqualityComparer<List<xyz>> { public bool Equals(List<xyz> x, List<xyz> y) { if (x == null && y == null) return true; if (x == null || y == null) return false; if (x.Count != y.Count) return false; // 需忽略课程顺序对分组的影响就用以下代码 return x.Select(c => c.course).OrderBy(c => c) .SequenceEqual(y.Select(c => c.course).OrderBy(c => c)); // 要求课程顺序完全一致才归为同一组就用以下代码替换上面的return // return x.Zip(y, (a, b) => a.course == b.course).All(res => res); } public int GetHashCode(List<xyz> obj) { if (obj == null) return 0; var hash = 17; // 和Equals逻辑对应,忽略顺序就保留OrderBy,否则去掉 foreach (var item in obj.Select(c => c.course).OrderBy(c => c)) { hash = hash * 23 + (item?.GetHashCode() ?? 0); } return hash; } }
调用GroupBy时传入比较器,同时投影为你需要的结构:
var result = projects.GroupBy(x => x.courselist, new CourseListComparer()) .Select(g => new { courselist = g.Key, ids = g.Select(a => a.id).ToList() }) .ToList();
方案2:快速实现(无需自定义类,适合简单场景)
将课程列表拼接为可直接比较的字符串作为分组键:
var result = projects.GroupBy(x => { // 忽略顺序就加OrderBy,要求顺序一致就去掉 var courses = x.courselist.Select(c => c.course).OrderBy(c => c); return string.Join("|", courses); }) .Select(g => new { courselist = g.First().courselist, ids = g.Select(a => a.id).ToList() }) .ToList();
注意
你给出的预期示例ID存在笔误,实际运行后第一组ID为[100, 103],第二组ID为[101]。
内容的提问来源于stack exchange,提问作者Anil
相关产品推荐
相关产品推荐

