2010-11-01 1 views
5

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

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.

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

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

Что я делаю неправильно?

ответ

9

Список1 содержит экземпляры 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)) 
+1

+1, если вы делаете поиск в 'list2' вместо' list1'. Или без поиска 'var listdiff = list1.Where (user =>! (List2.Any (user2 => user2.Id == user.Id));' –

+0

Yup заметил эту ошибку пару минут назад –

+0

+1: Хороший материал. Будет достаточно эффективным. – Ani

3

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

Пример:

var listDiff = list1 
    .AsEnumerable<object>() 
    .Except(list2 
     .AsEnumerable<object>() 
     .Select(row => ((dynamic)row).ID)); 
+0

Извините, забыл добавить, что я использую v3.5 – dstr

2

Если вы просто хотите в list1Ids, которые не являются в 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 берет на себя эту идею немного лучше; это не требует уникальности.

4

Не 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))); 
    } 
} 
2
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; 
      } 
     } 

    } 
0

Попробуйте

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

Добро пожаловать в [so]! Почему это работает? Те, кто может ответить на этот вопрос для себя, вероятно, не нуждаются в чей-то код. – jpaugh