Різниця між двома списками


118

У мене є два загальних списки, заповнені митними об'єктами.

Мені потрібно знайти різницю між тими двома списками (Елементи, які перебувають у першому без елементів у другому), у третьому.

Я думав, що використання .Except()було гарною ідеєю, але я не бачу, як це використовувати. Допомога!

c# 

Відповіді:


237

Використання Except- це абсолютно правильний шлях. Якщо ваш тип переосмислює Equalsі GetHashCode, або вас цікавить лише рівність еталонного типу (тобто дві посилання є лише "рівними", якщо вони стосуються точно такого ж об'єкта), ви можете просто використовувати:

var list3 = list1.Except(list2).ToList();

Якщо вам потрібно висловити власну ідею рівності, наприклад, за ідентифікацією, вам потрібно буде реалізувати IEqualityComparer<T>. Наприклад:

public class IdComparer : IEqualityComparer<CustomObject>
{
    public int GetHashCode(CustomObject co)
    {
        if (co == null)
        {
            return 0;
        }
        return co.Id.GetHashCode();
    }

    public bool Equals(CustomObject x1, CustomObject x2)
    {
        if (object.ReferenceEquals(x1, x2))
        {
            return true;
        }
        if (object.ReferenceEquals(x1, null) ||
            object.ReferenceEquals(x2, null))
        {
            return false;
        }
        return x1.Id == x2.Id;
    }
}

Потім використовуйте:

var list3 = list1.Except(list2, new IdComparer()).ToList();

Зауважте, що це видалить усі повторювані елементи. Якщо вам потрібні копії для збереження, можливо, найпростіше було б створити набір list2і використовувати щось на зразок:

var list3 = list1.Where(x => !set2.Contains(x)).ToList();

18
Як тільки в бік, я додам, що Exceptце операція Set , тоді в отриманому списку будуть виразні значення, наприклад {'A','A','B','C'}.Except({'B','C'}) повернення{'A'}
digEmAll

4
Я маю це: {'A', 'A', 'B', 'C'}. За винятком ({'B', 'C'}) повертається {'A'}, що працює ... однак {'B' , 'C'}. За винятком випадків ({'A', 'B', 'C'}) НЕ повертається {'A'}
MrLister

2
Різниця множини двох множин визначається як члени першого набору, які не відображаються у другому наборі (цитуючи MS). Отже, {B, C} .Екрім ({A, B, C}) нічого не повинно повернути, оскільки B і C обидва у другому наборі. Не помилка, більше стосується математичного визначення функції.
Стів Хібберт

Тож @JonSkeet, якщо я хочу порівняти два списки на основі, скажімо, 2 властивостей, я можу написати таким чином порівняння і отримати предмети, які не рівні?
Ehsan Sajjad

1
@NetMage: ОП заявило, що хочуть "Елементи, які знаходяться в першому, без предметів у другому" - це для мене звучить як різниця набору. Якщо перший список містить {5, 5, 5, 5, 1}, а другий список містить {5}, то в першому списку є лише 1, а не другий.
Джон Скіт

73

Ви можете зробити щось подібне:

var result = customlist.Where(p => !otherlist.Any(l => p.someproperty == l.someproperty));

це врятувало мені одну петлю передбачення.
alice7

12
Чи не повинен один із цих "l.someproperty" бути "p.someproperty"?
Манос Ділаверакіс

6
Це можна спростити за допомогою customlist.Where (p => otherlist.Any (l => p.someproperty! = L.someproperty));
Dhanuka777

@ Dhanuka777 це неправильно. AnyПовинно бути , Allякщо ви хочете зробити це так. Це тому, що другий список може містити елемент з першого списку, але якщо він не перший перевірений, він негайно отримає справжнє значення p.someproperty != l.someproperty. Це призводить до повернення елементів, які існують в обох списках. Ганьба 6 людей, які виступили за це.
Murphybro2

26

Я думаю, що важливо підкреслити - використання методу «Крім » поверне вам речі, які перебувають у першому, без предметів лише у другому. Він не повертає тих елементів у секунду, які не з’являються спочатку.

var list1 = new List<int> { 1, 2, 3, 4, 5};
var list2 = new List<int> { 3, 4, 5, 6, 7 };

var list3 = list1.Except(list2).ToList(); //list3 contains only 1, 2

Але якщо ви хочете отримати реальну різницю між двома списками:

Предмети, які перебувають у першому, без предметів у другому та предмети, які знаходяться у другому, без предметів у першому.

Вам потрібно використовувати крім двох разів:

var list1 = new List<int> { 1, 2, 3, 4, 5};
var list2 = new List<int> { 3, 4, 5, 6, 7 };

var list3 = list1.Except(list2); //list3 contains only 1, 2
var list4 = list2.Except(list1); //list4 contains only 6, 7
var resultList = list3.Concat(list4).ToList(); //resultList contains 1, 2, 6, 7

Або ви можете використовувати метод SymmetricExceptWith методу HashSet. Але це змінює набір, за яким викликається:

var list1 = new List<int> { 1, 2, 3, 4, 5};
var list2 = new List<int> { 3, 4, 5, 6, 7 };

var list1Set = list1.ToHashSet(); //.net framework 4.7.2 and .net core 2.0 and above otherwise new HashSet(list1)
list1Set.SymmetricExceptWith(list2);
var resultList = list1Set.ToList(); //resultList contains 1, 2, 6, 7

Це корисно. Тільки знайте, що ОП дала зрозуміти, що вони не хочуть повної різниці: "Мені потрібно знайти різницю між цими двома списками ( Елементи, які є в першому, без пунктів у другому )".
rsenna

10
var third = first.Except(second);

(Ви також можете зателефонувати ToList()після Except(), якщо вам не подобається посилання на ліниві колекції.)

Except()Метод порівнює значення , використовуючи компаратор за замовчуванням, якщо значення порівнюються мають базових типів даних, таких , як int, string, і decimalт.д.

Інакше порівняння буде здійснено за адресою об’єкта, що, мабуть, не те, що ви хочете ... У такому випадку зробіть власні об'єкти реалізованими IComparable(або реалізуйте звичай IEqualityComparerта передайте його Except()методу).


3
var list3 = list1.Where(x => !list2.Any(z => z.Id == x.Id)).ToList();

Примітка: list3міститиме елементи або об'єкти, які не в обох списках. Примітка: її ToList()немаєtoList()


1

Оскільки метод розширення Except працює на двох IEumerables, мені здається, що це буде операція O (n ^ 2). Якщо продуктивність є проблемою (якщо, скажімо, ваші списки є великими), я б запропонував створити HashSet з list1 та використовувати метод ExceptWith HashSet.


9
Enumerable.Exceptвнутрішньо використовує HashSetабо щось подібне. Він точно не використовує алгоритм наївного O (n ^ 2).
Джим Мішель

@Jim Mischel: Ви праві, Enumerable.Except використовує внутрішню структуру даних Set і додає елементи з обох IEnumerables до набору. Бажаю, щоб документація щось про це говорила.
foson

1

ось моє рішення:

    List<String> list1 = new List<String>();

    List<String> list2 = new List<String>();

    List<String> exceptValue = new List<String>();

foreach(String L1 in List1) 
{
    if(!List2.Contains(L1)
    {
         exceptValue.Add(L1);
    }
}
foreach(String L2 in List2) 
{
    if(!List1.Contains(L2)
    {
         exceptValue.Add(L2);
    }
}

-1

трохи пізно, але ось для мене працює рішення

 var myBaseProperty = (typeof(BaseClass)).GetProperties();//get base code properties
                    var allProperty = entity.GetProperties()[0].DeclaringType.GetProperties();//get derived class property plus base code as it is derived from it
                    var declaredClassProperties = allProperty.Where(x => !myBaseProperty.Any(l => l.Name == x.Name)).ToList();//get the difference

Згаданий вище код я отримую різницю властивостей між моїм базовим класом і списком похідних класів


-1
var resultList = checklist.Where(p => myList.All(l => p.value != l.value)).ToList();

Примітка: Список результатів - це елементи, які існують у контрольному списку, але не в моєму списку
Teezy7

-2
List<ObjectC> _list_DF_BW_ANB = new List<ObjectC>();    
List<ObjectA> _listA = new List<ObjectA>();
List<ObjectB> _listB = new List<ObjectB>();

foreach (var itemB in _listB )
{     
    var flat = 0;
    foreach(var itemA in _listA )
    {
        if(itemA.ProductId==itemB.ProductId)
        {
            flat = 1;
            break;
        }
    }
    if (flat == 0)
    {
        _list_DF_BW_ANB.Add(itemB);
    }
}

6 років після прийнятої відповіді і нічого нового не додає
Мітч Пшеничний

-3

Якщо обидва ваші списки реалізують інтерфейс IEnumerable, ви можете досягти цього за допомогою LINQ.

list3 = list1.where(i => !list2.contains(i));

6
І якщо кожен із ваших списків містить мільйон предметів, ви будете довго чекати. Використовуйте IEnumerable.Exceptзамість цього.
Джим Мішель

3
Так. У мене всього близько 450 предметів, і я все ще чекаю. Будьте обережні, використовуючи це.
Marnee KG7SIO

-3
        List<int> list1 = new List<int>();
        List<int> list2 = new List<int>();
        List<int> listDifference = new List<int>();

        foreach (var item1 in list1)
        {
            foreach (var item2 in list2)
            {
                if (item1 != item2)
                    listDifference.Add(item1);
            }
        }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.