IEnumerable.Except () между различными классами с общим полем - PullRequest
6 голосов
/ 01 ноября 2010

Можно ли использовать Except() для двух списков, которые имеют два разных класса, но имеют общее поле? У меня есть коллекции List<User1> и List<User2>. Они имеют разные свойства, кроме столбца Id, и я хочу найти разные записи между ними, используя этот столбец Id. Я пытаюсь использовать List<>.Except(), но я получаю эту ошибку:

Аргументы типа для метода System.Linq.Enumerable.Except (System.Collections.Generic.IEnumerable, System.Collections.Generic.IEnumerable) не могут быть выведены из использования. Попробуйте явно указать аргументы типа.

Вот что я пытаюсь:

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

Что я делаю не так?

Ответы [ 6 ]

9 голосов
/ 01 ноября 2010

List1 содержит экземпляры User1, а List2 содержит экземпляры User2.

Какой тип экземпляра должен быть создан list1.Except(list2.Select(row => row.Id))? Другими словами, если бы вывод типа не был доступен, что бы вы заменили var на?

Если User1 и User2 наследуются от одного и того же предка (с идентификатором), вместо этого используйте List<User>.

В противном случае:

var list2Lookup = list2.ToLookup(user => user.Id);
var listdiff = list1.Where(user => (!list2Lookup.Contains(user.Id))
4 голосов
/ 01 ноября 2010

Не Except, но правильные результаты и аналогичная производительность:

// 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));

И, конечно, вы можете обернуть все это в свой собственный метод расширения многократного использования:

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)));
    }
}
3 голосов
/ 01 ноября 2010

Вкратце, составьте списки для List<object> и используйте функцию C # из .NET 4.0: dynamic.

Пример:

var listDiff = list1
    .AsEnumerable<object>()
    .Except(list2
        .AsEnumerable<object>()
        .Select(row => ((dynamic)row).ID));
2 голосов
/ 01 марта 2016
public static IEnumerable<TSource> Except<TSource, CSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> TSelector, IEnumerable<CSource> csource, Func<CSource, TKey> CSelector)
    {
        bool EqualFlag = false;
        foreach (var s in source)
        {
            EqualFlag = false;
            foreach (var c in csource)
            {
                var svalue = TSelector(s);
                var cvalue = CSelector(c);
                if (svalue != null)
                {

                    if (svalue.Equals(cvalue))
                    {
                        EqualFlag = true;
                        break;
                    }
                }
                else if (svalue == null && cvalue == null)
                {
                    EqualFlag = true;
                    break;
                }
            }
            if (EqualFlag)
                continue;
            else
            {
                yield return s;
            }
        }

    }
2 голосов
/ 01 ноября 2010

Если вы просто хотите, чтобы Ids in list1 не были в list2, вы можете сделать:

var idsInList1NotInList2 = list1.Select(user1 => user1.Id)
                                .Except(list2.Select(user2 => user2.Id));

Если вам также нужны связанные объекты User1, вот один из способов(при условии, что идентификаторы уникальны для объекта User1):

// Create lookup from Id to the associated User1 object
var user1sById = list1.ToDictionary(user1 => user1.Id);

// Find Ids from the lookup that are not present for User2s from list2
// and then retrieve their associated User1s from the lookup
var user1sNotInList2 = user1sById.Keys
                                 .Except(list2.Select(user2 => user2.Id))
                                 .Select(key => user1sById[key]);

РЕДАКТИРОВАТЬ: vc74 принимает эту идею немного лучше;не требует уникальности.

0 голосов
/ 10 августа 2017

Попробуйте

list1.Where(user1 => !list2.Any(user2 => user2.Id.Equal(user1.Id)));
...