Reputation: 49
I can use Except()
on two same types like:
var list1 = new List<int> { 1 , 2 , 3 , 5 , 9 };
var list2 = new List<int> { 4 , 3 , 9 };
var expectedList = list1.Except(list2);//Result: { 1 , 2 , 5 }
But how to do with different types? like:
var cats = new List<Cat>();
var dogs = new List<Dog>();
var exceptCats = cats
.Except(dogs)
.On(cat.Age == dog.Age && cat.Name == dogs.Name)
Upvotes: 0
Views: 91
Reputation: 143098
You can use the Where
approach but for relatively big collections it can result in performance cost. To tackle this problem you can create custom equality comparer for the specific use case. If both types do not have shared type/interface (base class/interface) then it can look like the following:
class CatDogComparer : IEqualityComparer<object>
{
public bool Equals(object x, object y)
{
return (x, y) switch
{
(Dog d, Cat c) => Compare(d, c),
(Cat c, Dog d) => Compare(d, c),
_ => false
};
bool Compare(Dog d, Cat c) => d.Age == c.Age && d.Name == d.Name;
}
public int GetHashCode(object obj) => obj switch
{
Dog d => HashCode.Combine(d.Age, d.Name),
Cat c => HashCode.Combine(c.Age, c.Name),
_ => obj.GetHashCode() // throw?
};
}
And usage:
var exceptCats = cats
.Except(dogs, new CatDogComparer())
.ToList();
This can be improved by introducing some shared type like:
public interface IHaveNameAndAge
{
int Age { get; set; }
string Name { get; set; }
}
public class Cat : IHaveNameAndAge {...}
public class Dog : IHaveNameAndAge {...}
Another approach is to use LINQ (which as far as I remember should also be optimized):
var catsToExclude = from c in cats
join d in dogs on new { c.Age, c.Name } equals new { d.Age, d.Name }
select c;
var exceptCats = cats
.Except(catsToExclude)
.ToList();
Upvotes: 2
Reputation: 27436
Most performant is to use GroupJoin
var exceptCats =
from cat in cats
join dog in dogs
on new { cat.Age, cat.Name } equals { dog.Age, dog.Name } into gj
where !gj.Any()
select cat;
Upvotes: 2
Reputation: 24280
Let's rephrase: you want all cats for which no dog exists with the same name and age.
By stating it like that, the Linq you need to use comes almost naturally:
var cats = new List<Cat>();
var dogs = new List<Dog>();
var exceptCats = cats.Where(cat => !dogs.Any(dog => cat.Age == dog.Age && cat.Name == dog.Name));
Upvotes: 0
Reputation: 51420
Using Except()
for excluding the first set with the second set which both are the same type.
You can use .Where()
to filter in this case which has the same behavior.
var exceptCats = cats.Where(cat => !dogs.Any(dog => cat.Age == dog.Age && cat.Name == dog.Name))
.ToList();
Upvotes: 2