如何使用Linq在C#中过滤两个不同模型列表的差异数据
当然可以用LINQ实现这个需求!你当前用Join只能拿到两个列表中匹配的元素,但我们要的是差异项——也就是lstComplete里那些没出现在lstAssigned中的路由属性。
不过这里得先明确:判断两个元素是否“匹配”的依据是什么?从你的模型来看,RoutingAttributeModel和AgentRoutingAttributeModel都有Bus_No和Attribute_No,通常这两个字段应该是联合唯一标识,所以建议用这两个字段一起判断匹配(避免不同Bus下的同Attribute_No被误判)。如果确实只需要用Attribute_No,后面的代码你可以自行调整条件。
下面给你几种实用的实现方式:
方法1:Where + Any(最直观易懂)
这种方式直接遍历lstComplete,筛选出在lstAssigned中找不到对应匹配项的元素:
// 基于Bus_No和Attribute_No联合判断的差异项 var unassignedAttributes = lstComplete .Where(cl => !lstAssigned.Any(al => al.Bus_No == cl.Bus_No && al.Attribute_No == cl.Attribute_No)) .ToList();
如果只需要按Attribute_No判断,把条件改成al.Attribute_No == cl.Attribute_No就行。
方法2:Except(更简洁,但需要自定义比较器)
Except方法可以直接返回两个集合的差集,但因为我们用的是自定义类,得先实现一个IEqualityComparer来定义“相等”的规则:
// 定义比较器类,用于判断两个RoutingAttributeModel是否相等 public class RoutingAttributeComparer : IEqualityComparer<RoutingAttributeModel> { public bool Equals(RoutingAttributeModel x, RoutingAttributeModel y) { if (x == null || y == null) return false; // 这里用联合字段判断,你也可以改成只判断Attribute_No return x.Bus_No == y.Bus_No && x.Attribute_No == y.Attribute_No; } public int GetHashCode(RoutingAttributeModel obj) { // 对应Equals的规则生成哈希码,确保哈希值一致 return HashCode.Combine(obj.Bus_No, obj.Attribute_No); } }
然后把lstAssigned转换成RoutingAttributeModel类型,再用Except求差集:
// 先把Agent的分配列表转换成完整属性模型的格式 var assignedAsComplete = lstAssigned.Select(al => new RoutingAttributeModel { Bus_No = al.Bus_No, Attribute_No = al.Attribute_No, Attribute_Name = al.Attribute_Name, Status = al.Status, Notes = null // Agent模型没有Notes字段,这里设为null即可 }).ToList(); // 求差集:lstComplete里有但assignedAsComplete里没有的元素 var unassignedAttributes = lstComplete.Except(assignedAsComplete, new RoutingAttributeComparer()).ToList();
方法3:左外连接(延续你之前的Join思路)
你之前尝试了Join,其实用左外连接可以直接拿到所有lstComplete的元素,以及匹配的lstAssigned元素,然后筛选出没有匹配的那些:
var unassignedAttributes = from cl in lstComplete // 用匿名类型做联合键关联两个列表 join al in lstAssigned on new { cl.Bus_No, cl.Attribute_No } equals new { al.Bus_No, al.Attribute_No } into joinedGroup // 左外连接:如果没有匹配项,al会是null from al in joinedGroup.DefaultIfEmpty() // 筛选出没有匹配的元素 where al == null select cl;
这种写法和方法1的效果完全一致,只是用LINQ查询表达式的方式实现,更贴近你之前的尝试。
补充:如果需要双向差异
要是你需要的是双向差异(也就是lstComplete里有但lstAssigned没有的,加上lstAssigned里有但lstComplete没有的),可以把两个方向的差集合并:
// 先找lstComplete里未被分配的 var completeNotAssigned = lstComplete.Where(cl => !lstAssigned.Any(al => al.Bus_No == cl.Bus_No && al.Attribute_No == cl.Attribute_No)); // 再找lstAssigned里不在lstComplete中的(可能是已删除的属性还在分配列表里) var assignedNotComplete = lstAssigned.Where(al => !lstComplete.Any(cl => cl.Bus_No == al.Bus_No && cl.Attribute_No == al.Attribute_No)); // 合并两个结果(因为类型不同,转成object或者创建DTO类统一) var fullDiff = completeNotAssigned.Cast<object>().Concat(assignedNotComplete.Cast<object>()).ToList();
内容的提问来源于stack exchange,提问作者Felix A. Zelaya O.

