合并List<Result>并按Name/Class去重的实现及代码错误解决
我现在有两个List<Result>,每个Result包含Key和List<Value>。合并后发现部分Value的Name和Class存在重复,想知道怎么合并这些List<Value>,并且针对每个Key去除Name/Class重复的项?
我的代码示例:
var result1 = new List<Result> { new Result{Key = "X", Values = new List<Value>{new Value{Name = "A1", Class = "C1"}}}, new Result{Key = "Y", Values = new List<Value>{new Value{Name = "B1", Class = "D1"}}} }; var result2 = new List<Result> { new Result{Key = "X", Values = new List<Value>{new Value{Name = "A1", Class = "C1"}, new Value{Name = "A2", Class = "C1"}}}, new Result{Key = "Y", Values = new List<Value>{new Value{Name = "B2", Class = "D2"}}} };
我尝试的代码(有错误):
var lstResult = new List<Result>(); var groups = result1.Concat(result2).GroupBy(a => a.Key); foreach (var group in groups) { lstResult.Add(new Result { Key = group.Key, Values = group.Select(x => x.Values).ToList() }); } public class Result { public string Key { get; set; } public List<Value> Values { get; set; } } public class Value { public string Name { get; set; } public string Class { get; set; } }
遇到的错误:
无法将类型'System.Collections.Generic.List<System.Collections.Generic.List<net452.Program.Value>>'隐式转换为'System.Collections.Generic.List<net452.Program.Value>'
你的问题出在group.Select(x => x.Values)这里——这个操作会把每个分组里的Result的Values(也就是List<Value>)都取出来,得到的是一个IEnumerable<List<Value>>,直接ToList()后就是List<List<Value>>,和Result.Values需要的List<Value>类型不匹配。我们需要先展平这个嵌套的列表,再去重。
下面分两种常见场景给你解决方案:
方法一:使用SelectMany展平+自定义去重逻辑(兼容所有.NET版本)
首先,我们需要把分组里所有的Value列表展平成一个IEnumerable<Value>,然后根据Name和Class的组合去重。可以用GroupBy再取每组第一个元素实现去重:
代码实现:
var lstResult = new List<Result>(); var groups = result1.Concat(result2).GroupBy(a => a.Key); foreach (var group in groups) { // 先展平所有Values列表,再按Name+Class分组去重 var uniqueValues = group .SelectMany(r => r.Values) // 把嵌套的List<Value>展平成单个Value的序列 .GroupBy(v => new { v.Name, v.Class }) // 按Name和Class的组合分组 .Select(g => g.First()) // 取每组第一个元素(也就是去重后的结果) .ToList(); lstResult.Add(new Result { Key = group.Key, Values = uniqueValues }); }
方法二:使用DistinctBy(.NET Core 3.0+/NET 5+ 推荐)
如果你的项目使用的是.NET Core 3.0及以上版本,或者.NET 5+,可以直接用LINQ的DistinctBy方法,写法更简洁:
代码实现:
var lstResult = result1.Concat(result2) .GroupBy(r => r.Key) .Select(g => new Result { Key = g.Key, Values = g.SelectMany(r => r.Values) .DistinctBy(v => new { v.Name, v.Class }) // 直接按Name+Class去重 .ToList() }) .ToList();
额外优化:给Value类重写Equals和GetHashCode
如果以后经常需要对Value进行去重操作,最好在Value类里重写Equals和GetHashCode方法,这样可以直接使用Distinct()而不需要每次都写分组逻辑:
public class Value { public string Name { get; set; } public string Class { get; set; } public override bool Equals(object obj) { if (obj is Value other) { return Name == other.Name && Class == other.Class; } return false; } public override int GetHashCode() { // 组合Name和Class的哈希值 return HashCode.Combine(Name, Class); } }
之后去重就可以直接写:
Values = g.SelectMany(r => r.Values).Distinct().ToList()
内容的提问来源于stack exchange,提问作者user584018

