如何用C# Linq按特定条件实现GroupBy分组?
使用LINQ简化相同属性组合的Id分组逻辑
问题背景
你手头有这样一组数据表:
Id NameId ValueId 1 1 4 1 10 18 1 9 15 2 1 4 2 10 17 2 9 0 3 1 5 3 9 16 3 10 18 4 1 5 4 10 18 4 9 16 5 1 4 5 10 17 5 9 0
需要把所有对应NameId的ValueId完全一致的Id归为一组,你已经用嵌套循环实现了功能,但觉得代码繁琐,想知道怎么用LINQ简化。
你的现有精简代码如下:
var tableResult = _repository.GetData().OrderBy(x =>x.NameId).ToList(); var ids = tableResult.Select(x => x.id).Distinct().ToList(); var listOfProductGroup = new List<ProductGroup>(); for (int i = 0; i < ids.Count; i++) { var currentId = ids[i]; var currentEntity = tableResult.Where(x => x.id == currentId).ToList(); var productGroup = new ProductGroup(Guid.NewGuid(), currentId); for (int j = i + 1; j < ids.Count; j++) { var subsequentId = ids[j]; var subsequentEntity = tableResult.Where(x => x.id == subsequentId ).ToList(); //自定义的比较扩展方法 if(currentEntity.EqualsAll(subsequentEntity)) { productGroup.Id.Add(subsequentId ); ids.RemoveAt(j); j = j - 1; } } listOfProductGroup.Add(productGroup); } public class ProductGroup { public ProductGroup(Guid groupId, int id) { GroupId= groupId; Id= new List<int>() { id }; } public Guid GroupId{ get; set; } public IList<int> Id { get; set; } }
LINQ简化方案
当然可以用LINQ大幅简化!核心思路是:为每个Id生成一个能唯一标识其(NameId, ValueId)组合的"键",然后按这个键分组即可。
步骤1:定义辅助比较器(可选但推荐)
为了高效判断两个Id的(NameId, ValueId)集合是否完全一致,我们可以自定义一个相等比较器:
public class ValuePairListEqualityComparer : IEqualityComparer<List<(int NameId, int ValueId)>> { public bool Equals(List<(int NameId, int ValueId)> x, List<(int NameId, int ValueId)> y) { //先判断数量是否一致 if (x.Count != y.Count) return false; //逐个比较每个(NameId, ValueId)对 for (int i = 0; i < x.Count; i++) { if (x[i].NameId != y[i].NameId || x[i].ValueId != y[i].ValueId) return false; } return true; } public int GetHashCode(List<(int NameId, int ValueId)> obj) { //生成哈希值,保证相同的组合有相同的哈希 int hash = 17; foreach (var pair in obj) { hash = hash * 31 + pair.NameId.GetHashCode(); hash = hash * 31 + pair.ValueId.GetHashCode(); } return hash; } }
步骤2:用LINQ实现分组
//先获取原始数据 var tableResult = _repository.GetData().ToList(); //1. 按Id分组,得到每个Id对应的(NameId, ValueId)集合,并按NameId排序(保证顺序一致) var idToValuePairs = tableResult .GroupBy(entity => entity.Id) .ToDictionary( group => group.Key, group => group.OrderBy(e => e.NameId) .Select(e => (e.NameId, e.ValueId)) .ToList() ); //2. 按ValuePair的组合分组,生成ProductGroup var comparer = new ValuePairListEqualityComparer(); var productGroups = idToValuePairs .GroupBy(kvp => kvp.Value, comparer) .Select(group => new ProductGroup(Guid.NewGuid(), group.Select(kvp => kvp.Key).ToList())) .ToList();
步骤3:调整ProductGroup构造函数
为了适配批量传入Id列表,建议给ProductGroup添加一个重载构造函数:
public class ProductGroup { //保留原有构造函数,兼容旧代码 public ProductGroup(Guid groupId, int id) { GroupId = groupId; Id = new List<int>() { id }; } //新增构造函数,支持传入Id列表 public ProductGroup(Guid groupId, List<int> ids) { GroupId = groupId; Id = ids; } public Guid GroupId { get; set; } public IList<int> Id { get; set; } }
方案优势
- 代码更简洁:去掉了嵌套循环和手动维护索引的逻辑,可读性大幅提升
- 更健壮:避免了手动操作列表时容易出现的索引越界、重复处理等问题
- 可扩展性强:如果后续需要修改分组规则,只需调整比较器或键的生成逻辑即可
内容的提问来源于stack exchange,提问作者LearningToCode
相关产品推荐
相关产品推荐

