Spen D
Spen D

Reputation: 4345

compare two list and return not matching items using linq

i have a two list

List<Sent> SentList;
List<Messages> MsgList;

both have the same property called MsgID;

MsgList            SentList  

MsgID Content      MsgID Content Stauts
1       aaa        1       aaa     0
2       bbb        3       ccc     0
3       ccc        
4       ddd
5       eee

i want to compare the MsgID in Msglist with the sentlist and need items which are not in the sent list using linq

Result 

MsgID Content
2       bbb
4       ddd
5       eee

Upvotes: 46

Views: 160660

Answers (12)

Ramiz Koyum
Ramiz Koyum

Reputation: 1

Make a single list:

var result = list.GroupBy(x => x.BillId).Where(x => x.Count() == 1).Select(x => x.First());

Upvotes: 0

Astrid E.
Astrid E.

Reputation: 2872

In .NET 6 you can take advantage of .ExceptBy(), which lets you define which property of the first list to compare the items in the second list by:

List<Message> result = messages
    .ExceptBy(sentList.Select(msg => msg.MsgID), msg => msg.MsgID)
    .ToList();

messages is the first list, whereas a collection of the MsgID properties from sentList is the second list.

Example fiddle here.


Note:
.ExceptBy() produces the set difference between the two collections --> only distinct values will be in the resulting collection. This means that if messages contains the same value more than once (e.g. { "aaa", "bbb", "ccc", "ddd", "ddd", "eee" }), any duplicates will be removed in the resulting collection (--> { "bbb", "ddd", "eee" }).

Upvotes: 6

Pooyan Tavakoli
Pooyan Tavakoli

Reputation: 21

If u wanna Select items of List from 2nd list:

MainList.Where(p => 2ndlist.Contains(p.columns from MainList )).ToList();

Upvotes: 0

Sapnandu
Sapnandu

Reputation: 642

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();

Person class

public class Person
{        
    public int Id { get; set; }       

    public string Name { get; set; }
}

Upvotes: 0

Matt Searles
Matt Searles

Reputation: 2765

As an extension method

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);
}

eg.

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);

Upvotes: 0

Tushar patel
Tushar patel

Reputation: 3407

You can do like this,this is the quickest process

Var result = MsgList.Except(MsgList.Where(o => SentList.Select(s => s.MsgID).ToList().Contains(o.MsgID))).ToList();

This will give you expected output.

Upvotes: 10

Jignesh Thakker
Jignesh Thakker

Reputation: 3698

Try,

  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>();

Hope it should help.

Upvotes: 3

lc.
lc.

Reputation: 116498

The naive approach:

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

Be aware this will take up to m*n operations as it compares every MsgID in SentList to each in MsgList ("up to" because it will short-circuit when it does happen to match).

Upvotes: 35

Andre Calil
Andre Calil

Reputation: 7692

Well, you already have good answers, but they're most Lambda. A more LINQ approach would be like

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

Upvotes: 21

Tys
Tys

Reputation: 3610

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;
}

Upvotes: -1

Eric J.
Eric J.

Reputation: 150108

You can do something like

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

You will need to provide a custom equality comparer as outlined on MSDN

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

Simply have that equality comparer base equality only on MsgID property of each respective type. Since the equality comparer compares two instances of the same type, you would need to define an interface or common base type that both Sent and Messages implement that has a MsgID property.

Upvotes: 9

Reed Copsey
Reed Copsey

Reputation: 564433

You could do something like:

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

var results = MsgList.Where(m => !sentIDs.Contains(m.MsgID));

This will return all messages in MsgList which don't have a matching ID in SentList.

Upvotes: 56

Related Questions