比较两个列表并使用 linq 返回不匹配的项目
Posted
技术标签:
【中文标题】比较两个列表并使用 linq 返回不匹配的项目【英文标题】:compare two list and return not matching items using linq 【发布时间】:2012-08-14 17:33:14 【问题描述】:我有两个列表
List<Sent> SentList;
List<Messages> MsgList;
两者都具有称为 MsgID 的相同属性;
MsgList SentList
MsgID Content MsgID Content Stauts
1 aaa 1 aaa 0
2 bbb 3 ccc 0
3 ccc
4 ddd
5 eee
我想将 Msglist 中的 MsgID 与 sentlist 进行比较,并需要使用 linq 不在已发送列表中的项目
Result
MsgID Content
2 bbb
4 ddd
5 eee
【问题讨论】:
别忘了将帮助您解决问题的答案标记为正确 =) 【参考方案1】:你可以这样做:
HashSet<int> sentIDs = new HashSet<int>(SentList.Select(s => s.MsgID));
var results = MsgList.Where(m => !sentIDs.Contains(m.MsgID));
这将返回MsgList
中没有SentList
中匹配ID 的所有消息。
【讨论】:
这可能是最好的方法 如果您需要比较多个属性怎么办? (例如 ID 和版本号? @NickG 然后为它创建一个实现 IEqualityComparerHashSet<className>
。天真的方法:
MsgList.Where(x => !SentList.Any(y => y.MsgID == x.MsgID))
请注意,这将需要最多 m*n
操作,因为它将 SentList
中的每个 MsgID
与 MsgList
中的每个 MsgList
进行比较(“最多”,因为它会在它发生时短路 em> 恰好匹配)。
【讨论】:
如果你要投反对票,你至少可以向我解释原因。 怎么样:MsgList.Where(x => SentList.All(y => y.MsgID == x.MsgID))
@zumalifeguard 我想你的意思是!=
,如果是的话,它是一个逻辑上等价的表达式,应该花费相同的时间。【参考方案3】:
嗯,你已经有了很好的答案,但它们大多是 Lambda。更多的 LINQ 方法就像
var NotSentMessages =
from msg in MsgList
where !SentList.Any(x => x.MsgID == msg.MsgID)
select msg;
【讨论】:
var NotSentMessages = from msg in MsgList where !SentList.Any(x => x.MsgID == msg.MsgID && x.content == msg.content) select msg;
这更适合比较多个属性(组合键)
@AndrewDay 是的,但 OP 说他想检查相等的 ID,所以这里不需要匹配字符串。
- 所以你的代码是更好的代码,因为它是可扩展的。但这是一个不好的评论????
@AndrewDay 根本不是人,我只是在解释为什么我只匹配 ID。 :)
@AndreCalil Calil:LINQ 支持两种语法...“查询语法和方法语法在语义上是相同的...”来自docs.microsoft.com/en-us/dotnet/csharp/programming-guide/…【参考方案4】:
你可以这样做,这是最快的过程
Var result = MsgList.Except(MsgList.Where(o => SentList.Select(s => s.MsgID).ToList().Contains(o.MsgID))).ToList();
这将为您提供预期的输出。
【讨论】:
我很确定您会在 Where 函数的每次迭代中从 SentList 中创建一个列表。没有办法有效。首先将SentList.Select(s => s.MsgID).ToList()
存储在 where 语句之外的变量中。【参考方案5】:
你可以这样做
var notSent = MsgSent.Except(MsgList, MsgIdEqualityComparer);
您需要提供 MSDN 上概述的自定义相等比较器
http://msdn.microsoft.com/en-us/library/bb336390.aspx
只需让相等比较器基于每个相应类型的 MsgID 属性的相等性。由于相等比较器比较相同类型的两个实例,因此您需要定义 Sent 和 Messages 都实现的具有 MsgID 的接口或公共基类型 属性。
【讨论】:
【参考方案6】:试试,
public class Sent
public int MsgID;
public string Content;
public int Status;
public class Messages
public int MsgID;
public string Content;
List<Sent> SentList = new List<Sent>() new Sent() MsgID = 1, Content = "aaa", Status = 0 , new Sent() MsgID = 3, Content = "ccc", Status = 0 ;
List<Messages> MsgList = new List<Messages>() new Messages() MsgID = 1, Content = "aaa" , new Messages() MsgID = 2, Content = "bbb" , new Messages() MsgID = 3, Content = "ccc" , new Messages() MsgID = 4, Content = "ddd" , new Messages() MsgID = 5, Content = "eee" ;
int [] sentMsgIDs = SentList.Select(v => v.MsgID).ToArray();
List<Messages> result1 = MsgList.Where(o => !sentMsgIDs.Contains(o.MsgID)).ToList<Messages>();
希望它会有所帮助。
【讨论】:
【参考方案7】:作为扩展方法
public static IEnumerable<TSource> AreNotEqual<TSource, TKey, TTarget>(this IEnumerable<TSource> source, Func<TSource, TKey> sourceKeySelector, IEnumerable<TTarget> target, Func<TTarget, TKey> targetKeySelector)
var targetValues = new HashSet<TKey>(target.Select(targetKeySelector));
return source.Where(sourceValue => targetValues.Contains(sourceKeySelector(sourceValue)) == false);
例如。
public class Customer
public int CustomerId get; set;
public class OtherCustomer
public int Id get; set;
var customers = new List<Customer>()
new Customer() CustomerId = 1 ,
new Customer() CustomerId = 2
;
var others = new List<OtherCustomer>()
new OtherCustomer() Id = 2 ,
new OtherCustomer() Id = 3
;
var result = customers.AreNotEqual(customer => customer.CustomerId, others, other => other.Id).ToList();
Debug.Assert(result.Count == 1);
Debug.Assert(result[0].CustomerId == 1);
【讨论】:
是否可以为选择器使用多个属性? @Rebecca 我认为这可行:) var result = customers .AreNotEqual(customer => (customer.CustomerId, customer.CustomerName), others, other => (other.Id, other.Name )) .ToList();【参考方案8】:List<Person> persons1 = new List<Person>
new Person Id = 1, Name = "Person 1",
new Person Id = 2, Name = "Person 2",
new Person Id = 3, Name = "Person 3",
new Person Id = 4, Name = "Person 4"
;
List<Person> persons2 = new List<Person>
new Person Id = 1, Name = "Person 1",
new Person Id = 2, Name = "Person 2",
new Person Id = 3, Name = "Person 3",
new Person Id = 4, Name = "Person 4",
new Person Id = 5, Name = "Person 5",
new Person Id = 6, Name = "Person 6",
new Person Id = 7, Name = "Person 7"
;
var output = (from ps1 in persons1
from ps2 in persons2
where ps1.Id == ps2.Id
select ps2.Name).ToList();
人物类
public class Person
public int Id get; set;
public string Name get; set;
【讨论】:
【参考方案9】:如果你想从第二个列表中选择列表项:
MainList.Where(p => 2ndlist.Contains(p.columns from MainList )).ToList();
【讨论】:
【参考方案10】:List<Car> cars = new List<Car>() new Car() Name = "Ford", Year = 1892, Website = "www.ford.us" ,
new Car() Name = "Jaguar", Year = 1892, Website = "www.jaguar.co.uk" ,
new Car() Name = "Honda", Year = 1892, Website = "www.honda.jp" ;
List<Factory> factories = new List<Factory>() new Factory() Name = "Ferrari", Website = "www.ferrari.it" ,
new Factory() Name = "Jaguar", Website = "www.jaguar.co.uk" ,
new Factory() Name = "BMW", Website = "www.bmw.de" ;
foreach (Car car in cars.Where(c => !factories.Any(f => f.Name == c.Name)))
lblDebug.Text += car.Name;
【讨论】:
啊,和lc完全一样的解决方案。张贴。但是,这里有一个完整且经过测试的示例。以上是关于比较两个列表并使用 linq 返回不匹配的项目的主要内容,如果未能解决你的问题,请参考以下文章