C# 比较两个列表并使用linq返回不匹配的项

C# 比较两个列表并使用linq返回不匹配的项,c#,linq,list,c#-4.0,C#,Linq,List,C# 4.0,我有两张单子 List<Sent> SentList; List<Messages> MsgList; 我想将Msglist中的MsgID与sentlist进行比较,并需要使用linq将不在发送列表中的项目进行比较 Result MsgID Content 2 bbb 4 ddd 5 eee 天真的方法: MsgList.Where(x => !SentList.Any(y => y.MsgID == x.MsgID

我有两张单子

List<Sent> SentList;
List<Messages> MsgList;
我想将Msglist中的MsgID与sentlist进行比较,并需要使用linq将不在发送列表中的项目进行比较

Result 

MsgID Content
2       bbb
4       ddd
5       eee
天真的方法:

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

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

您可以执行以下操作:

HashSet<int> sentIDs = new HashSet<int>(SentList.Select(s => s.MsgID));

var results = MsgList.Where(m => !sentIDs.Contains(m.MsgID));
HashSet sentIDs=newhashset(SentList.Select(s=>s.MsgID));
var results=MsgList.Where(m=>!sentIDs.Contains(m.MsgID));

这将返回
MsgList
中所有在
SentList
中没有匹配ID的消息。您可以执行以下操作

var notSent = MsgSent.Except(MsgList, MsgIdEqualityComparer);
您将需要提供MSDN上概述的自定义相等比较器

只需让相等比较器仅在每个相应类型的MsgID属性上建立相等。由于相等比较器比较同一类型的两个实例,因此您需要定义一个接口或公共基类型,该接口或公共基类型由发送和消息实现,并具有MsgID属性。

List cars=new List(){new Car(){Name=“Ford”,Year=1892,Website=“www.Ford.us”},
新车(){Name=“Jaguar”,年份=1892,网址=“www.Jaguar.co.uk”},
新车(){Name=“Honda”,Year=1892,网址=“www.Honda.jp”};
List factories=new List(){new Factory(){Name=“Ferrari”,Website=“www.Ferrari.it”},
新工厂(){Name=“Jaguar”,网站=“www.Jaguar.co.uk”},
新工厂(){Name=“BMW”,网址=“www.BMW.de”};
foreach(cars-Car-in-cars.Where(c=>!factories.Any)(f=>f.Name==c.Name))){
lblDebug.Text+=car.Name;
}

好吧,你已经有了很好的答案,但它们都是很好的答案。更为LINQ的方法如下

var NotSentMessages =
                from msg in MsgList
                where !SentList.Any(x => x.MsgID == msg.MsgID)
                select msg;
试试看

已发送公共类
{
公共int MsgID;
公共字符串内容;
公众地位;
}
公共类消息
{
公共int MsgID;
公共字符串内容;
}
List SentList=new List(){new Sent(){MsgID=1,Content=“aaa”,Status=0},new Sent(){MsgID=3,Content=“ccc”,Status=0};
List MsgList=new List(){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 result1=MsgList.Where(o=>!sentMsgIDs.Contains(o.MsgID)).ToList();

希望能有所帮助。

你可以这样做,这是最快的过程

Var result = MsgList.Except(MsgList.Where(o => SentList.Select(s => s.MsgID).ToList().Contains(o.MsgID))).ToList();
这将为您提供预期的输出。

作为扩展方法

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);
}
公共静态IEnumerable AreNotEqual(此IEnumerable源、Func sourceKeySelector、IEnumerable目标、Func targetKeySelector)
{
var targetValues=newhashset(target.Select(targetKeySelector));
返回source.Where(sourceValue=>targetValues.Contains(sourceKeySelector(sourceValue))==false);
}
例如

公共类客户
{
public int CustomerId{get;set;}
}
公共类客户
{
公共int Id{get;set;}
}
var customers=新列表()
{
新客户(){CustomerId=1},
新客户(){CustomerId=2}
};
var others=新列表()
{
新建OtherCustomer(){Id=2},
新建OtherCustomer(){Id=3}
};
var result=customers.AreNotEqual(customer=>customer.CustomerId,others,other=>other.Id).ToList();
Assert(result.Count==1);
Assert(结果[0]。CustomerId==1);

如果要从第二个列表中选择列表中的项目:


MainList.Where(p=>2ndlist.Contains(MainList中的p.columns)).ToList()

这可能是最好的方法如果你需要比较多个属性呢?(例如Id和版本号?@NickG,然后你为它创建一个类,实现IEqualityComparer,并生成一个
哈希集
。啊,与lc.posted完全相同的解决方案。但是,好吧,这里有一个完整且经过测试的示例。如果你要投反对票,你至少可以礼貌地向我解释一下原因。怎么样:
MsgList.Where(x=>SentList.All(y=>y.MsgID==x.MsgID))
@zumalifeguard我想你的意思是
!=
,如果是的话,它是一个逻辑上等价的表达式,应该花费相同的时间。别忘了将帮助你解决问题的答案标记为正确=)
var NotSentMessages=来自MsgList中的msg where!任何(x=>x.MsgID==msg.MsgID&&x.content==msg.content)选择msg
这更适合于比较多个属性(组合键)@AndrewDay是的,但是OP说他想检查相等的ID,所以这里没有必要匹配字符串。-所以你的代码更好,因为它是可扩展的。但这是一个糟糕的评论???@AndrewDay一点也不,伙计,我只是解释为什么我只匹配ID.:)@AndreCalil:LINQ支持两种语法。。。“查询语法和方法语法在语义上是相同的…”从中可以为选择器使用多个属性吗?@Rebecca我认为这会起作用:)var result=customers.AreNotEqual(customer=>(customer.CustomerId,customer.CustomerName),other,other=>(other.Id,other.Name)).ToList();
  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>();
Var result = MsgList.Except(MsgList.Where(o => SentList.Select(s => s.MsgID).ToList().Contains(o.MsgID))).ToList();
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);
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; }
}