C#中使用LINQ检测并移除对象列表中的重复项
解决ErrorData列表基于内容的重复项检测与移除问题
你的代码失效的核心原因是:默认的引用类型比较是基于对象引用而非内容。LocalizationListData是自定义引用类型,List<KeyValuePair<string, string>>同样是引用类型,即使两个实例的内容完全一致,只要是不同的对象实例,就会被判定为不同的分组键,导致GroupBy无法正确识别重复项。
下面提供两种可靠的解决方案:
方法一:自定义相等比较器(推荐,通用且性能优)
实现IEqualityComparer<ErrorData>,自定义ErrorData的相等逻辑,直接使用Distinct方法完成去重和重复检测。
步骤1:实现自定义相等比较器
public class ErrorDataEqualityComparer : IEqualityComparer<ErrorData> { public bool Equals(ErrorData x, ErrorData y) { // 处理null情况 if (ReferenceEquals(x, y)) return true; if (x is null || y is null) return false; // 先比较Severity和Category if (x.Severity != y.Severity || x.Category != y.Category) return false; // 比较Localisations内部的键值对列表 var xLocList = x.Localisations?.LstLocalizationData; var yLocList = y.Localisations?.LstLocalizationData; if (xLocList is null && yLocList is null) return true; if (xLocList is null || yLocList is null) return false; if (xLocList.Count != yLocList.Count) return false; // 若键值对顺序不重要,先排序再比较;顺序重要则直接用SequenceEqual var sortedX = xLocList.OrderBy(kv => kv.Key).ThenBy(kv => kv.Value).ToList(); var sortedY = yLocList.OrderBy(kv => kv.Key).ThenBy(kv => kv.Value).ToList(); return sortedX.SequenceEqual(sortedY); } public int GetHashCode(ErrorData obj) { if (obj is null) return 0; // 组合计算哈希值,确保内容相同的对象哈希一致 int hash = 17; hash = hash * 23 + (obj.Severity?.GetHashCode() ?? 0); hash = hash * 23 + (obj.Category?.GetHashCode() ?? 0); if (obj.Localisations?.LstLocalizationData != null) { // 同样,若顺序不重要则先排序再计算哈希 var sortedList = obj.Localisations.LstLocalizationData.OrderBy(kv => kv.Key).ThenBy(kv => kv.Value); foreach (var kv in sortedList) { hash = hash * 23 + kv.Key.GetHashCode(); hash = hash * 23 + kv.Value.GetHashCode(); } } return hash; } }
步骤2:使用比较器完成去重和检测
List<ErrorData> errList = new List<ErrorData>(); // 填充errList数据... var comparer = new ErrorDataEqualityComparer(); // 检测是否存在重复项 bool hasDups = errList.Count != errList.Distinct(comparer).Count(); // 移除重复项 errList = errList.Distinct(comparer).ToList();
方法二:基于内容生成分组键(快速实现,适合简单场景)
将Localisations的键值对列表转换为可被默认比较器识别的字符串(或其他值类型结构),作为GroupBy的键的一部分。
List<ErrorData> errList = new List<ErrorData>(); // 填充errList数据... var groupErr = errList.GroupBy(x => new { x.Severity, x.Category, // 将键值对排序后拼接成唯一字符串,作为分组依据 LocalizationKey = x.Localisations?.LstLocalizationData != null ? string.Join("|", x.Localisations.LstLocalizationData .OrderBy(kv => kv.Key) .ThenBy(kv => kv.Value) .Select(kv => $"{kv.Key}:{kv.Value}")) : null }); // 检测重复项 bool hasDups = groupErr.Any(g => g.Count() > 1); // 移除重复项 if (hasDups) { errList = groupErr.Select(g => g.First()).ToList(); }
注意:如果键或值中包含拼接使用的分隔符(如|),需要替换为不会冲突的特殊字符,避免生成错误的重复键。
内容的提问来源于stack exchange,提问作者JerryW
相关产品推荐
相关产品推荐

