如何对SelectMany后的AzureADUser列表按ObjectId+SourceGroups按需去重?
需求:去除AzureADUser的完全重复实例(保留同ObjectId但不同SourceGroups的实例)
现有类定义
public class GroupMembership { public List<AzureADUser> SourceMembers { get; set; } } public class AzureADUser { public Guid ObjectId { get; set; } public List<Guid> SourceGroups { get; set; } }
执行var source = groupsMemberships.SelectMany(x => x.SourceMembers).ToList();后,结果包含不符合预期的重复实例:我们需要保留同一ObjectId但SourceGroups不同的AzureADUser实例,仅去除ObjectId和SourceGroups均完全相同的重复项。
示例输入
var users1 = new List<AzureADUser> { new () { ObjectId = new Guid("Guid1"), SourceGroups = new List<Guid> {new Guid("GuidG1")}}, new () { ObjectId = new Guid("Guid2"), SourceGroups = new List<Guid> {new Guid("GuidG2")}}, new () { ObjectId = new Guid("Guid3"), SourceGroups = new List<Guid> {new Guid("GuidG3")}}, new () { ObjectId = new Guid("Guid1"), SourceGroups = new List<Guid> {new Guid("GuidG4")}} // 需保留 }; var users2 = new List<AzureADUser> { new () { ObjectId = new Guid("Guid1"), SourceGroups = new List<Guid> {new Guid("GuidG1")}} // 需去重 }; var groupMembership1 = new GroupMembership { SourceMembers = users1 }; var groupMembership2 = new GroupMembership { SourceMembers = users2 }; var groupsMemberships = new List<GroupMembership> { groupMembership1, groupMembership2 };
期望输出
/* ObjectId: new Guid("Guid1"), SourceGroups: new List<Guid> {new Guid("GuidG1")} ObjectId: new Guid("Guid2"), SourceGroups: new List<Guid> {new Guid("GuidG2")} ObjectId: new Guid("Guid3"), SourceGroups: new List<Guid> {new Guid("GuidG3")} ObjectId: new Guid("Guid1"), SourceGroups: new List<Guid> {new Guid("GuidG4")} */
解决方案
方法1:自定义相等比较器配合Distinct
创建实现IEqualityComparer<AzureADUser>的比较器,明确判断两个实例是否完全相同的逻辑:
public class AzureADUserEqualityComparer : IEqualityComparer<AzureADUser> { public bool Equals(AzureADUser x, AzureADUser y) { if (ReferenceEquals(x, y)) return true; if (x == null || y == null) return false; // 先匹配ObjectId if (x.ObjectId != y.ObjectId) return false; // 再匹配SourceGroups(排序后比较,避免顺序影响判断) if (x.SourceGroups == null && y.SourceGroups == null) return true; if (x.SourceGroups == null || y.SourceGroups == null) return false; return x.SourceGroups.OrderBy(g => g).SequenceEqual(y.SourceGroups.OrderBy(g => g)); } public int GetHashCode(AzureADUser obj) { if (obj == null) return 0; var hash = obj.ObjectId.GetHashCode(); // 结合排序后的SourceGroups哈希值,保证相同组集合的哈希一致 if (obj.SourceGroups != null) { foreach (var guid in obj.SourceGroups.OrderBy(g => g)) { hash = hash * 31 ^ guid.GetHashCode(); } } return hash; } }
修改查询代码,传入自定义比较器:
var source = groupsMemberships .SelectMany(x => x.SourceMembers) .Distinct(new AzureADUserEqualityComparer()) .ToList();
方法2:LINQ GroupBy分组去重
无需单独定义比较器,通过匿名类型分组实现去重:
var source = groupsMemberships .SelectMany(x => x.SourceMembers) .GroupBy(u => new { u.ObjectId, // 将排序后的组ID拼接成字符串,作为分组标识 SourceGroupsKey = string.Join("|", u.SourceGroups?.OrderBy(g => g) ?? Enumerable.Empty<Guid>()) }) .Select(g => g.First()) // 每组取第一个实例 .ToList();
方法3:重写AzureADUser的Equals和GetHashCode
如果该相等判断逻辑是全局通用的,直接在类中重写方法:
public class AzureADUser { public Guid ObjectId { get; set; } public List<Guid> SourceGroups { get; set; } public override bool Equals(object obj) { return Equals(obj as AzureADUser); } public bool Equals(AzureADUser other) { if (other == null) return false; if (ObjectId != other.ObjectId) return false; if (SourceGroups == null && other.SourceGroups == null) return true; if (SourceGroups == null || other.SourceGroups == null) return false; return SourceGroups.OrderBy(g => g).SequenceEqual(other.SourceGroups.OrderBy(g => g)); } public override int GetHashCode() { var hash = ObjectId.GetHashCode(); if (SourceGroups != null) { foreach (var guid in SourceGroups.OrderBy(g => g)) { hash = hash * 31 ^ guid.GetHashCode(); } } return hash; } }
之后直接调用Distinct即可:
var source = groupsMemberships .SelectMany(x => x.SourceMembers) .Distinct() .ToList();
内容的提问来源于stack exchange,提问作者user989988
相关产品推荐
相关产品推荐

