Use Linq to Get Items in One List≪≫, That Are Not in Another List≪≫

Use LINQ to get items in one List , that are not in another List

This can be addressed using the following LINQ expression:

var result = peopleList2.Where(p => !peopleList1.Any(p2 => p2.ID == p.ID));

An alternate way of expressing this via LINQ, which some developers find more readable:

var result = peopleList2.Where(p => peopleList1.All(p2 => p2.ID != p.ID));

Warning: As noted in the comments, these approaches mandate an O(n*m) operation. That may be fine, but could introduce performance issues, and especially if the data set is quite large. If this doesn't satisfy your performance requirements, you may need to evaluate other options. Since the stated requirement is for a solution in LINQ, however, those options aren't explored here. As always, evaluate any approach against the performance requirements your project might have.


LINQ - Find all items in one list that aren't in another list

Try using .Except extension method (docs):

var result = list1.Except(list2);

will give you all items in list1 that are not in list2.

IMPORTANT: Even though there's a link provided to MSDN docs for the method, I'll point this out here: Except only works out of the box for collections of primitive types, for POCOs/objects you need to implement IEquatable on that object.

Use LINQ to get items in one List , that are in another List

var result = peopleList2.Where(p => peopleList1.Any(p2 => p2.ID == p.ID));

Filter a list based on another list containing IEnumerable Guid using linq

You want items Where it's not true that the list Contains the id:

var filteredList = ListA.Where(x => !listOfGuids.Contains(x.id))

LINQ query to find if items in a list are contained in another list


var test2NotInTest1 = test2.Where(t2 => test1.Count(t1 => t2.Contains(t1))==0);

Faster version as per Tim's suggestion:

var test2NotInTest1 = test2.Where(t2 => !test1.Any(t1 => t2.Contains(t1)));

how to compare every value in a list with an other list by linq


var SameNames = ListA.All(x => ListB.All(y => y.Name.Equals(x.Name)));

What you wrote here is a requirement that all names in A match all names in B. That clearly conflicts with your example data, as the "Marc" from A does not equal "Jolie" from B.

Your current code would only return true if A and B in total only contain one distinct name. Which makes no sense for the problem you're trying to solve.


What you're actually trying to solve is if all names from A have one match in B. That would be achieved by doing:

var sameNames = listA.All(a => listB.Any(b => b.Name.Equals(a.Name)));

Note the Any. It returns true if it find (at least) one match. This is different from All, which only returns true if all values in B are a match.

However, this is not enough yet. You've now ascertained that all names from A appear in B, but it's still possible that B contains more names that aren't in A. Take the following example:

var listA = new List<string>() { "Andy", "Bobby", "Cindy" };
var listB = new List<string>() { "Andy", "Bobby", "Cindy", "David" };

var SameNames = listA.All(a => listB.Any(b => b.Name.Equals(a.Name)));

SameNames will be true, but that's because you only one direction. You need to check in both directions:

var listA_in_listB = listA.All(a => listB.Any(b => b.Name.Equals(a.Name)));
var listB_in_listA = listB.All(b => listA.Any(a => a.Name.Equals(b.Name)));

var sameNames = listA_in_listB && listB_in_listA;

This gives you the result you want.


Note that there are a few other variations on how to approach this.

If you can guarantee that each list does not contain any duplicates, then instead of doing the same check twice, you can simple do one of the checks and then confirm that the lists are of equal length:

var listA_in_listB = listA.All(a => listB.Any(b => b.Name.Equals(a.Name)));
var sameLength = listA.Length == listB.Length;

var sameNames = listA_in_listB && sameLength;

This is more efficient, but it does require knowing that your names are unique within each list.

Linq filtering a list by a composite key (contains and not contains)

You can do more complex comparisons using Any() rather than Select().Contains(). For instance, filtering if the composite key exists in secondList goes like this:

.Where(x => !secondList.Any(y => y.docNum == x.docNum && y.docVersion == x.docVersion)

This is slightly more efficient in this case than Select().Contains() since there's no intermediate object construction. In some cases where the intermediate object is more complex the savings go up.

I'd recommend that you use Any() for your first condition as well, giving you something like:

firstList
.Where(x => x.Any(y => y.docNum == x.docNum))
.Where(x => !secondList.Any(y => y.docNum == x.docNum && y.docVersion == x.docVersion)

The first Where() will filter out any items that don't have a matching docNum in the list, the second will filter the remaining items to remove those that have a full composite match on the second list.

Of course this is terribly inefficient since you have to scan secondList twice - partially, but still two scans. It would be better to do a lookup for version numbers keyed on document number and use that to do much more efficient filtering:

var lu = secondList.ToLookup(y => y.docNum, y => y.docVersion);

firstList.Where
(
x =>
{
var l = lu[x.docNum];
return l.Any() && !l.Any(v => v == x.docVersion);
}
)

Since ILookup<> is reasonably well optimized the actual time taken to do the filter is greatly reduced. Using some very simple (mostly-)random list generation with reasonably high collisions I tested filtering 10,000 items against a list of 1,000,000 items to see what the difference would be. 49 seconds for the first option, 1.8 seconds with a lookup.

That said, this is really only going to work well on IEnumerable<>. If you're operating on an IQueryable<> then stick with Any() and good indices.

Filter a list based on another list condition

Let's say you have a class like below:

class CustomerInformation
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}

As you said, you have two lists, let's say you have two lists like below:

List<CustomerInformation> dbList = new List<CustomerInformation>
{
new CustomerInformation{Id=1, FirstName="Raju", LastName="Ahmed"},
new CustomerInformation{Id=2, FirstName="Tahira", LastName="Biswas"},
new CustomerInformation{Id=3, FirstName="Shohag", LastName="Mia"},
new CustomerInformation{Id=4, FirstName="Saiful", LastName="Islam"}
};

List<CustomerInformation> csutomerList = new List<CustomerInformation>
{
new CustomerInformation{Id=1, FirstName="Raju", LastName="Ahmed"},
new CustomerInformation{Id=2, FirstName="Tahira", LastName="Biswas"},
new CustomerInformation{Id=3, FirstName="Shohag", LastName="Mia"},
new CustomerInformation{Id=4, FirstName="Saiful", LastName="Islam"},
new CustomerInformation{Id=5, FirstName="Anny", LastName="Bishwas"},
new CustomerInformation{Id=6, FirstName="Kabita", LastName="Roy"},
new CustomerInformation{Id=7, FirstName="Zahidul", LastName="Emon"}
};

Now you want to get those DB list that are not present in the customer list with somespecific condition, so just try this:

 var newList = csutomerList.Where(cusItem => !dbList.Any(dbItem => cusItem.Id == dbItem.Id && cusItem.FirstName == dbItem.FirstName && cusItem.LastName == dbItem.LastName));

it will first find out all the data that are present in both lists and then simply deduct them.

Sample Output:

Sample Image

Full code here:

    static void Main(string[] args)
{
AvailableData();
Console.ReadKey();
}

public static void AvailableData()
{
// Create two lists.
List<CustomerInformation> dbList = new List<CustomerInformation>
{
new CustomerInformation{Id=1, FirstName="Raju", LastName="Ahmed"},
new CustomerInformation{Id=2, FirstName="Tahira", LastName="Biswas"},
new CustomerInformation{Id=3, FirstName="Shohag", LastName="Mia"},
new CustomerInformation{Id=4, FirstName="Saiful", LastName="Islam"}
};

List<CustomerInformation> csutomerList = new List<CustomerInformation>
{
new CustomerInformation{Id=1, FirstName="Raju", LastName="Ahmed"},
new CustomerInformation{Id=2, FirstName="Tahira", LastName="Biswas"},
new CustomerInformation{Id=3, FirstName="Shohag", LastName="Mia"},
new CustomerInformation{Id=4, FirstName="Saiful", LastName="Islam"},
new CustomerInformation{Id=5, FirstName="Anny", LastName="Bishwas"},
new CustomerInformation{Id=6, FirstName="Kabita", LastName="Roy"},
new CustomerInformation{Id=7, FirstName="Zahidul", LastName="Emon"}
};


var newList = csutomerList.Where(cusItem => !dbList.Any(dbItem => cusItem.Id == dbItem.Id && cusItem.FirstName == dbItem.FirstName && cusItem.LastName == dbItem.LastName));

foreach (var cust in newList)
{
Console.WriteLine("Customer Id :{0} | Customer Name: {1} | Last Name: {2} ",cust.Id,cust.FirstName,cust.LastName);
}
Console.ReadKey();

}
}

class CustomerInformation
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}


Related Topics



Leave a reply



Submit