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:
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
Insert 2 Million Rows into SQL Server Quickly
How to Export Datatable to Excel
Ado.Net |Datadirectory| Where Is This Documented
Convert Json String to C# Object
Why Does the Ef 6 Tutorial Use Asynchronous Calls
Integer Summing Blues, Short += Short Problem
Dynamic Where Clause (Or) in Linq to Entities
Tips For Optimizing C#/.Net Programs
Difference Between a Regular String and a Verbatim String
Serialport Not Receiving Any Data
Multiline String Literal in C#
Decimal Precision and Scale in Ef Code First
Still Confused About Covariance and Contravariance & In/Out
Is There Any Significant Difference Between Using If/Else and Switch-Case in C#