比较两个列表并使用 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 然后为它创建一个实现 IEqualityComparer 的类,并改为创建一个HashSet&lt;className&gt;【参考方案2】:

天真的方法:

MsgList.Where(x => !SentList.Any(y => y.MsgID == x.MsgID))

请注意,这将需要最多 m*n 操作,因为它将 SentList 中的每个 MsgIDMsgList 中的每个 MsgList 进行比较(“最多”,因为它会在它发生时短路 em> 恰好匹配)。

【讨论】:

如果你要投反对票,你至少可以向我解释原因。 怎么样:MsgList.Where(x =&gt; SentList.All(y =&gt; 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 =&gt; x.MsgID == msg.MsgID &amp;&amp; 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 =&gt; s.MsgID).ToList() 存储在 where 语句之外的变量中。【参考方案5】:

你可以这样做

var notSent = MsgSent.Except(MsgList, MsgIdEqualityComparer);

您需要提供 MSDN 上概述的自定义相等比较器

http://msdn.microsoft.com/en-us/library/bb336390.aspx

只需让相等比较器基于每个相应类型的 MsgID 属性的相等性。由于相等比较器比较相同类型的两个实例,因此您需要定义 SentMessages 都实现的具有 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 返回不匹配的项目的主要内容,如果未能解决你的问题,请参考以下文章

在添加列表之前如何检查项目的值不等于零?

在 Linq 中使用另一个列表中的项目搜索列表

Python:在元组列表和嵌套列表中比较并查找匹配项

如何比较python中的两个列表并返回匹配项

如何根据多个条件并使用 linq 从通用列表中删除项目

比较python中的两个列表并返回匹配值的索引