Видаліть елементи з одного списку в інший


206

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

Тож скажімо, я маю це як гіпотетичний приклад

List<car> list1 = GetTheList();
List<car> list2 = GetSomeOtherList();

Я хочу обмістити list1 за допомогою foreach і видалити кожен елемент у List1, який також міститься в List2.

Я не зовсім впевнений, як це зробити, оскільки foreach не базується на індексі.


1
Ви хочете видалити елементи в List1, які також є в List2?
Шрінівас Редді Татіпарті

1
Що має статися, якщо у вас є list1 = {foo1} і list2 = {foo1, foo1}. Чи повинні бути видалені всі копії foo1 зі списку2, або лише перша?
Марк Байерс

2
-1 - Я відповів на кожну відповідь на це запитання, тому що вважав, що вони помиляються, але, здається, питання просто задається жахливо. Тепер я не можу їх змінити - вибачте. Ви хочете видалити елементи, list1які існують у list2, або ви хочете видалити елементи, list2які існують у list1? Під час цього коментаря кожна надана відповідь буде виконувати останню.
Джон Раш

7
@John Rashch, ти повинен бути трішечки меншим задоволенням у цих суботах. Деякі відповіді досить концептуальні і лише демонструють, як досягти того, чого хоче ОП, навіть не стосуючись списків, зазначених у питанні.
Жоао Анджело

3
@ Марк - ти маєш рацію, моя вина повністю - саме тому я поставив тут коментар, пояснюючи, що сталося, я шукав попередню відповідь, у мене вже було схоже запитання тим часом після мого голосування і збирався піти коментарі після того, як я його знайшов - виявляється, це не найкращий процес для цього!
Джон Раш

Відповіді:


358

Ви можете використовувати крім :

List<car> list1 = GetTheList();
List<car> list2 = GetSomeOtherList();
List<car> result = list2.Except(list1).ToList();

Напевно, вам навіть не потрібні такі тимчасові змінні:

List<car> result = GetSomeOtherList().Except(GetTheList()).ToList();

Зауважте, що Except не змінюється жоден список - він створює новий список з результатом.


13
Незначна точка, але це призведе до IEnumerable<car>, а не до List<car>. Щоб ToList()повернути список, вам потрібно зателефонувати . Окрім того, я вважаю, що це має бутиGetSomeOtherList().Except(GetTheList()).ToList()
Адам Робінсон

9
Вам також знадобиться, using System.Linq;якщо у вас його раніше не було.
yellavon

1
Примітка: list1.Except (list2) не дасть такого ж результату, як list2.Except (list1). Останній працював на мене.
radbyx

2
Будьте обережні при використанні, Exceptоскільки це фактично виконує задану операцію, яка відрізняє отриманий список. Я не очікував такої поведінки, оскільки використовую, а Listне a HashSet. Пов'язані.
logan

4
Звідки це правильна відповідь? Звичайно, це може дати вам те, що ви хочете, у вашому контексті, однак, "Видалити елементи з одного списку в інший", безумовно, не рівнозначно встановленій різниці операцій, і ви не повинні дезінформувати людей, приймаючи це як правильну відповідь !!!!
користувач1935724

37

Індекс вам не потрібен, оскільки List<T>клас дозволяє видаляти елементи за значенням, а не за допомогою індексу за допомогою Removeфункції.

foreach(car item in list1) list2.Remove(item);

3
+1, але IMO слід використовувати дужки навколо list2.Remove(item);заяви.
ANeves

2
@sr pt: Я завжди використовую дужки для операторів, які з'являються в іншому рядку, але не для блоків з одним твердженням, які я можу / робити, розміщуючи в тому ж рядку, що і оператор управління потоком.
Адам Робінсон

4
@uriz: Ігнорування кваліфікації , що було б шикарно, це єдина відповідь , який на самому ділі робить те , що питання говорить (видаляє елементи в головному списку); інша відповідь створює новий список, який може бути небажаним, якщо список передається від іншого абонента, який очікує його зміни замість отримання списку заміни.
Адам Робінсон

5
@uriz @AdamRobinson, оскільки ми обговорюємо елегантні рішення ...list1.ForEach(c => list2.Remove(c));
Девід Шеррет,

1
"елегантний" повинен означати, що "розробник, який дотримується цього коду, знайде його простим і зрозумілим", саме тому це найкраща відповідь.
Сет

22

Я рекомендую використовувати методи розширення LINQ . Ви можете легко зробити це за допомогою одного рядка коду так:

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

Це, якщо звичайно, об'єкти в list1, які ви видаляєте зі list2, є тим самим екземпляром.


2
Він також видаляє дублікати.
липеньОсновний

17

У моєму випадку у мене було два різних списки із загальним ідентифікатором, схожим на іноземний ключ. Друге рішення, на яке посилається "nzrytmn" :

var result =  list1.Where(p => !list2.Any(x => x.ID == p.ID && x.property1 == p.property1)).ToList();

Був тим, хто найкраще вписувався в мою ситуацію. Мені потрібно було завантажити DropDownList без записів, які вже були зареєстровані.

Дякую !!!

Це мій код:

t1 = new T1();
t2 = new T2();

List<T1> list1 = t1.getList();
List<T2> list2 = t2.getList();

ddlT3.DataSource= list2.Where(s => !list1.Any(p => p.Id == s.ID)).ToList();
ddlT3.DataTextField = "AnyThing";
ddlT3.DataValueField = "IdAnyThing";
ddlT3.DataBind();

ou ніколи не пояснював, що таке DDlT3
rogue39nin

15

Ви можете використовувати LINQ, але я б пішов із RemoveAllметодом. Я думаю, що саме це краще виражає ваші наміри.

var integers = new List<int> { 1, 2, 3, 4, 5 };

var remove = new List<int> { 1, 3, 5 };

integers.RemoveAll(i => remove.Contains(i));

9
Або навіть простіше з групами методів, які ви можете зробити - integers.RemoveAll (remove.Contains);
Райан

12
list1.RemoveAll(l => list2.Contains(l));

aka "абсолютно нечистий" :-)
Ксан Кун Кларк-Девіс

Що з цим не так Це виглядає краще, ніж створити інший список, використовуючи Крім. Особливо, коли обидва списки дуже малі.
Майк Кескінов

1
Оскільки обидва методи списку є O(N), це призведе до O(N^2)виникнення проблеми з великими списками.
tigrou

7

Рішення 1: Ви можете зробити так:

List<car> result = GetSomeOtherList().Except(GetTheList()).ToList();

Але в деяких випадках це рішення може не спрацювати. якщо це не працює, ви можете використовувати моє друге рішення.

Рішення 2:

List<car> list1 = GetTheList();
List<car> list2 = GetSomeOtherList();

ми робимо вигляд, що list1 - ваш основний список, а list2 - ваш другий список, і ви хочете отримати елементи list1 без елементів списку2.

 var result =  list1.Where(p => !list2.Any(x => x.ID == p.ID && x.property1 == p.property1)).ToList();

0

Оскільки список Exceptне змінюється, ви можете використовувати ForEach у List<T>:

list2.ForEach(item => list1.Remove(item));

Це може бути не найефективнішим способом, але він простий, тому читабельний, і він оновлює оригінальний список (що є моєю вимогою).


-3

Ось ви йдете ..

    List<string> list = new List<string>() { "1", "2", "3" };
    List<string> remove = new List<string>() { "2" };

    list.ForEach(s =>
        {
            if (remove.Contains(s))
            {
                list.Remove(s);
            }
        });

3
-1. Це викине виняток після видалення першого елемента. Крім того, краща ідея перейти до списку для видалення , оскільки він зазвичай менший. Ви також змушуєте більше траверсів списків робити це таким чином.
Адам Робінсон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.