Reputation: 19933
I have a List<Class1>
and List<Class2>
, and I'd like get all the items from List<Class1>
where the item's Id exists in List<Class2>
, and, conversely, get all the items from List<Class1>
where the item's Id does not exist in List<Class2>
.
public class Class1
{
public int Id { get; set; }
public string AnotherId { get; set; }
public decimal Price { get; set; }
}
public class Class2
{
public int Id { get; set; }
public int AnotherId { get; set; }
public string Message { get; set; }
}
I tried several solution with Contains
but don't find the right way to do it.
Upvotes: 0
Views: 59
Reputation: 23796
If you have a small number of items in your lists, the answer by Christos is the simplest. However, it is an O(n^2) algorithm. If you have a decent number of items in your lists (even in the few thousands), that algorithm will significantly slow down. Let's try an example:
List<Class1> list1 = new List<Class1>();
List<Class2> list2 = new List<Class2>();
const int ITEMS_TO_ADD = 30000;
for (int i=1; i<= ITEMS_TO_ADD; i++)
{
list1.Add(new Class1 { Id = i });
list2.Add(new Class2 { Id = i });
}
for (int i = 1; i <= ITEMS_TO_ADD; i++)
{
list1.Add(new Class1 { Id = -i });
}
Stopwatch sw = new Stopwatch();
sw.Start();
var result = list1.Where(x => list2.Any(y => y.Id == x.Id)).ToList();
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
On my machine, that takes around 30 seconds.
A better solution, again, if you have a decent sized data set would be something like this:
HashSet<int> list2Keys = new HashSet<int>(list2.Select(x => x.Id));
var resultInList2 = list1.Where(x => list2Keys.Contains(x.Id)).ToList();
Which on my machine takes about 10 milliseconds.
Of course, to get items NOT in list 2 you simply change the line to
var resultInList2 = list1.Where(x => !list2Keys.Contains(x.Id)).ToList();
This is O(n) of course as opposed to O(n^2)
Upvotes: 0
Reputation: 37070
There are several ways to do this, the simplest way would be to use the System.Linq
methods, Where()
, All()
, and Any()
:
var matchingIds = list1
.Where(list1Item => list2.Any(list2Item => list2Item.Id == list1Item.Id))
.ToList();
var nonMatching = list1
.Where(list1Item => list2.All(list2Item => list2Item.Id != list1Item.Id))
.ToList();
But it's also easy to do with a simple loop:
var matchingIds = new List<Class1>();
var nonMatching = new List<Class1>();
foreach (var list1Item in list1)
{
var foundMatch = false;
foreach (var list2Item in list2)
{
if (list2Item.Id == list1Item.Id)
{
foundMatch = true;
break;
}
}
if (foundMatch)
{
matchingIds.Add(list1Item);
}
else
{
nonMatching.Add(list1Item);
}
}
Upvotes: 0
Reputation: 122
Try something like this :
foreach(var val in listClass2)
{
result.AddRange(listClass1.Where(l => l.id = val.id));
}
Upvotes: 0
Reputation: 53958
You could try something like this:
var result = list1.Where(x => list2.Any(y=>y.Id == x.Id))
.ToList();
Even if both lists contained references to objects of the same type, Contains
wouldn't work out of the box. Had it been this the case, you would have either implement the interface IEquatable
for your class or override both Equals
and GetHashCode
methods. Only then Contains
would work as you expected to.
Upvotes: 3