You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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; }
}

方案优势

  1. 代码更简洁:去掉了嵌套循环和手动维护索引的逻辑,可读性大幅提升
  2. 更健壮:避免了手动操作列表时容易出现的索引越界、重复处理等问题
  3. 可扩展性强:如果后续需要修改分组规则,只需调整比较器或键的生成逻辑即可

内容的提问来源于stack exchange,提问作者LearningToCode

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:07:57