Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IEnumerable.Except() between different classes with a common field

Is it possible to use Except() for two List's that have two different classes but a common field? I have List<User1> and List<User2> collections. They have different properties except Id column and I want to find the different records between them using this Id column. I'm trying to use List<>.Except() but I'm getting this error:

The type arguments for method 'System.Linq.Enumerable.Except(System.Collections.Generic.IEnumerable, System.Collections.Generic.IEnumerable)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

Here's what I'm trying:

List<User1> list1 = List1();
List<User2> list2 = List2();
var listdiff = list1.Except(list2.Select(row => row.Id));

What am I doing wrong?

like image 652
dstr Avatar asked Nov 01 '10 15:11

dstr


3 Answers

List1 contains instances of User1 and List2 contains instances of User2.

What type of instance should be produced by list1.Except(list2.Select(row => row.Id))? In other words if type inference was not available, what would you replace var with?

If User1 and User2 inherit from the same ancestor (with ID), use List<User> instead.

Otherwise:

var list2Lookup = list2.ToLookup(user => user.Id);
var listdiff = list1.Where(user => (!list2Lookup.Contains(user.Id))
like image 187
vc 74 Avatar answered Nov 11 '22 22:11

vc 74


Not Except, but the correct results and similar performance:

// assumes that the Id property is an Int32
var tempKeys = new HashSet<int>(list2.Select(x => x.Id));
var listdiff = list1.Where(x => tempKeys.Add(x.Id));

And, of course, you can wrap it all up in your own re-usable extension method:

var listdiff = list1.Except(list2, x => x.Id, y => y.Id);

// ...

public static class EnumerableExtensions
{
    public static IEnumerable<TFirst> Except<TFirst, TSecond, TKey>(
        this IEnumerable<TFirst> first,
        IEnumerable<TSecond> second,
        Func<TFirst, TKey> firstKeySelector,
        Func<TSecond, TKey> secondKeySelector)
    {
        // argument null checking etc omitted for brevity

        var keys = new HashSet<TKey>(second.Select(secondKeySelector));
        return first.Where(x => keys.Add(firstKeySelector(x)));
    }
}
like image 4
LukeH Avatar answered Nov 11 '22 22:11

LukeH


Briefly, make lists to be List<object> and use C# feature from .NET 4.0: dynamic.

Example:

var listDiff = list1
    .AsEnumerable<object>()
    .Except(list2
        .AsEnumerable<object>()
        .Select(row => ((dynamic)row).ID));
like image 3
abatishchev Avatar answered Nov 11 '22 22:11

abatishchev