Hapus item dari satu daftar di daftar lain

206

Saya mencoba mencari cara untuk menelusuri daftar item umum yang ingin saya hapus dari daftar item lain.

Jadi katakanlah saya memiliki ini sebagai contoh hipotetis

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

Saya ingin melintasi list1 dengan foreach dan menghapus setiap item di List1 yang juga ada di List2.

Saya tidak yakin bagaimana cara melakukannya karena foreach tidak berbasis indeks.

PositiveGuy
sumber
1
Anda ingin menghapus item di List1 yang juga ada di List2?
Srinivas Reddy Thatiparthy
1
Apa yang harus terjadi jika Anda memiliki list1 = {foo1} dan list2 = {foo1, foo1}. Haruskah semua salinan foo1 dihapus dari list2, atau hanya yang pertama?
Mark Byers
2
-1 - Saya telah menurunkan semua jawaban dalam pertanyaan ini karena saya pikir mereka semua salah, tetapi tampaknya pertanyaannya hanya ditanyakan secara mengerikan. Sekarang, saya tidak bisa mengubahnya - permintaan maaf. Apakah Anda ingin menghapus item dari list1yang ada di list2, atau Anda ingin menghapus item dari list2yang ada di list1? Pada saat komentar ini, setiap jawaban yang diberikan akan melakukan yang terakhir.
John Rasch
7
@ John Rashch, Anda seharusnya menjadi pemicu yang sedikit kurang senang pada downvotes itu. Beberapa jawaban cukup konseptual dan hanya menunjukkan bagaimana mencapai apa yang diinginkan OP tanpa berhubungan dengan daftar yang disebutkan dalam pertanyaan.
João Angelo
3
@ Mark - Anda benar, kesalahan saya sepenuhnya - itu sebabnya saya memberikan komentar di sini untuk menjelaskan apa yang terjadi, saya sedang mencari jawaban sebelumnya saya sudah memiliki pertanyaan serupa sementara itu setelah pemungutan suara saya dan akan pergi komentar setelah saya menemukannya - ternyata itu bukan proses terbaik untuk ini!
John Rasch

Jawaban:

358

Anda dapat menggunakan Kecuali :

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

Anda mungkin bahkan tidak membutuhkan variabel sementara itu:

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

Catatan yang Excepttidak mengubah daftar baik - itu membuat daftar baru dengan hasilnya.

Mark Byers
sumber
13
Poin minor, tetapi ini akan menghasilkan IEnumerable<car>, bukan a List<car>. Anda perlu menelepon ToList()untuk mendapatkan daftar kembali. Selain itu, saya percaya seharusnyaGetSomeOtherList().Except(GetTheList()).ToList()
Adam Robinson
9
Anda juga perlu using System.Linq;jika Anda tidak memilikinya sebelumnya.
yellavon
1
Catatan: list1.Except (list2) tidak akan memberikan hasil yang sama dengan list2.Except (list1). Yang terakhir berhasil untuk saya.
radbyx
2
Berhati-hatilah saat menggunakan Exceptkarena ini benar-benar melakukan operasi yang ditetapkan , yang membedakan daftar yang dihasilkan. Saya tidak mengharapkan perilaku ini karena saya menggunakan List, bukan a HashSet. Terkait
logan
4
Kenapa ini jawaban yang benar? Tentu ini mungkin memberi Anda apa yang Anda inginkan dalam konteks Anda, namun, "Hapus item dari satu daftar di daftar lain" tentu saja tidak setara dengan operasi perbedaan set, dan Anda tidak boleh salah informasi orang dengan menerima ini sebagai jawaban yang tepat !!!!
user1935724
37

Anda tidak perlu indeks, karena List<T>kelas memungkinkan Anda untuk menghapus item berdasarkan nilai daripada indeks dengan menggunakan Removefungsi.

foreach(car item in list1) list2.Remove(item);
Adam Robinson
sumber
3
+1, tetapi IMO Anda harus menggunakan tanda kurung di sekitar list2.Remove(item);pernyataan.
ANeves
2
@ sr pt: Saya selalu menggunakan tanda kurung pada pernyataan yang muncul di baris lain, tetapi tidak pada blok pernyataan tunggal yang saya bisa / lakukan tempat pada baris yang sama dengan pernyataan kontrol aliran.
Adam Robinson
4
@uriz: Mengabaikan kualifikasi apa yang akan menjadi elegan, ini adalah satu-satunya jawaban yang benar-benar melakukan apa yang dikatakan pertanyaan (menghilangkan item dalam daftar utama); jawaban yang lain membuat daftar baru , yang mungkin tidak diinginkan jika daftar tersebut diteruskan dari penelepon yang berbeda yang mengharapkannya untuk diubah daripada mendapatkan daftar pengganti.
Adam Robinson
5
@uriz @AdamRobinson karena kita sedang membahas solusi elegan ...list1.ForEach(c => list2.Remove(c));
David Sherret
1
"Elegan" harus berarti "pengembang yang terjebak mempertahankan kode ini akan merasa sederhana dan mudah dimengerti," itulah sebabnya ini adalah jawaban terbaik.
Seth
22

Saya akan merekomendasikan menggunakan metode ekstensi LINQ . Anda dapat dengan mudah melakukannya dengan satu baris kode seperti:

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

Ini dengan asumsi tentu saja objek di list1 yang Anda hapus dari list2 adalah contoh yang sama.

Berkshire
sumber
2
Ini menghapus duplikat juga.
JulyOrdinary
17

Dalam kasus saya, saya memiliki dua daftar yang berbeda, dengan pengidentifikasi umum, seperti kunci asing. Solusi kedua yang dikutip oleh "nzrytmn" :

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

Adalah orang yang paling cocok dengan situasi saya. Saya perlu memuat DropDownList tanpa catatan yang sudah terdaftar.

Terima kasih !!!

Ini kode saya:

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();
Gabriel Santos Reis
sumber
Anda tidak pernah menjelaskan apa itu
DDlT3
15

Anda bisa menggunakan LINQ, tapi saya akan menggunakan RemoveAllmetode. Saya pikir itu adalah salah satu yang lebih baik mengekspresikan niat Anda.

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

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

integers.RemoveAll(i => remove.Contains(i));
João Angelo
sumber
9
Atau bahkan lebih sederhana dengan grup metode yang dapat Anda lakukan - integers.RemoveAll (remove.Contains);
Ryan
12
list1.RemoveAll(l => list2.Contains(l));
Alexandre Amado de Castro
sumber
alias "sama sekali tidak murni" :-)
Xan-Kun Clark-Davis
Apa yang salah dengannya? Terlihat lebih baik daripada membuat daftar lain menggunakan Kecuali. Terutama ketika kedua daftar itu sangat kecil.
Mike Keskinov
1
Karena kedua metode daftar adalah O(N), ini akan mengarah pada O(N^2)yang mungkin menjadi masalah dengan daftar besar.
tigrou
7

Solusi 1: Anda dapat melakukan ini:

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

Tetapi dalam beberapa kasus mungkin solusi ini tidak berfungsi. jika tidak berhasil, Anda dapat menggunakan solusi kedua saya.

Solusi 2:

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

kami berpura-pura bahwa list1 adalah daftar utama Anda dan list2 adalah daftar secondry Anda dan Anda ingin mendapatkan item dari list1 tanpa item dari list2.

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

Karena Excepttidak mengubah daftar, Anda dapat menggunakan ForEach di List<T>:

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

Ini mungkin bukan cara yang paling efisien, tetapi sederhana, karena itu dapat dibaca, dan memperbarui daftar asli (yang merupakan persyaratan saya).

Necriis
sumber
-3

Ini dia ..

    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);
            }
        });
Ian P
sumber
3
-1. Ini akan mengeluarkan pengecualian setelah item pertama dihapus. Juga, itu (umumnya) ide yang lebih baik untuk melintasi daftar untuk dihapus , karena biasanya lebih kecil. Anda juga memaksa lebih banyak daftar lintas yang melakukannya dengan cara ini.
Adam Robinson