Xóa các mục khỏi danh sách này trong danh sách khác


206

Tôi đang cố gắng tìm ra cách duyệt qua danh sách chung các mục mà tôi muốn xóa khỏi danh sách các mục khác.

Vì vậy, hãy nói rằng tôi có điều này như một ví dụ giả thuyết

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

Tôi muốn duyệt qua list1 bằng một foreach và xóa từng mục trong List1 cũng được chứa trong List2.

Tôi không chắc chắn làm thế nào để đi về điều đó vì foreach không dựa trên chỉ mục.


1
Bạn muốn xóa các mục trong List1 cũng nằm trong List2?
Srinivas Reddy Thatiparthy

1
Điều gì sẽ xảy ra nếu bạn có list1 = {foo1} và list2 = {foo1, foo1}. Tất cả các bản sao của foo1 nên được xóa khỏi list2, hay chỉ là bản đầu tiên?
Mark Byers

2
-1 - Tôi đã đánh giá thấp mọi câu trả lời trong câu hỏi này vì tôi nghĩ rằng tất cả chúng đều sai, nhưng có vẻ như câu hỏi chỉ được hỏi một cách khủng khiếp. Bây giờ, tôi không thể thay đổi chúng - lời xin lỗi. Bạn có muốn xóa các mục từ list1tồn tại trong đó list2, hoặc bạn muốn xóa các mục từ list2đó tồn tại trong list1? Tại thời điểm nhận xét này, mỗi câu trả lời được cung cấp sẽ thực hiện sau.
John Rasch

7
@ John Rashch, bạn nên bớt một chút kích hoạt hạnh phúc trên những downvote đó. Một số câu trả lời khá khái niệm và chỉ trình bày cách đạt được những gì OP muốn mà không liên quan đến các danh sách được đề cập trong câu hỏi.
João Angelo

3
@Mark - bạn nói đúng, lỗi của tôi hoàn toàn - đó là lý do tại sao tôi đưa bình luận lên đây để giải thích những gì đã xảy ra, tôi đang tìm kiếm một câu trả lời trước đó tôi đã có một câu hỏi tương tự sau khi bỏ phiếu và sẽ rời đi bình luận sau khi tôi tìm thấy nó - hóa ra đó không phải là quá trình tốt nhất cho việc này!
John Rasch

Câu trả lời:


358

Bạn có thể sử dụng Ngoại trừ :

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

Bạn có thể thậm chí không cần các biến tạm thời đó:

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

Lưu ý rằng Exceptkhông sửa đổi một trong hai danh sách - nó tạo ra một danh sách mới với kết quả.


13
Điểm nhỏ, nhưng điều này sẽ tạo ra một IEnumerable<car>, không phải a List<car>. Bạn cần gọi ToList()để lấy lại danh sách. Ngoài ra, tôi tin rằng nó nên như vậyGetSomeOtherList().Except(GetTheList()).ToList()
Adam Robinson

9
Bạn cũng sẽ cần using System.Linq;nếu bạn không có nó trước đây.
yellavon 11/03/2015

1
Lưu ý: list1.Except (list2) sẽ không cho kết quả giống như list2.Except (list1). Việc cuối cùng làm việc cho tôi.
radbyx

2
Chỉ cần cẩn thận khi sử dụng Exceptvì điều này thực sự thực hiện một thao tác thiết lập , làm phân biệt danh sách kết quả. Tôi không mong đợi hành vi này vì tôi đang sử dụng a List, không phải a HashSet. Liên quan.
logan

4
Làm thế nào đây là câu trả lời đúng? Chắc chắn điều này có thể cung cấp cho bạn những gì bạn muốn trong ngữ cảnh của mình, tuy nhiên, "Xóa các mục khỏi danh sách này trong danh sách khác" chắc chắn không tương đương với thao tác khác biệt đã đặt và bạn không nên hiểu sai về mọi người bằng cách chấp nhận đây là câu trả lời đúng !!!!
dùng1935724

37

Bạn không cần một chỉ mục, vì List<T>lớp cho phép bạn loại bỏ các mục theo giá trị thay vì chỉ mục bằng cách sử dụng Removehàm.

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

3
+1, nhưng IMO bạn nên sử dụng dấu ngoặc quanh list2.Remove(item);câu lệnh.
ANeves

2
@sr pt: Tôi luôn sử dụng dấu ngoặc trên các câu lệnh xuất hiện trên một dòng khác, nhưng không phải trên các khối lệnh đơn mà tôi có thể / thực hiện trên cùng một dòng với câu lệnh điều khiển luồng.
Adam Robinson

4
@uriz: Không quan tâm đến trình độ của những gì sẽ thanh lịch, đây là câu trả lời duy nhất thực sự làm những gì câu hỏi nói (loại bỏ các mục trong danh sách chính); câu trả lời khác tạo ra một danh sách mới , có thể không được mong muốn nếu danh sách đó được truyền từ một người gọi khác đang mong đợi nó được sửa đổi thay vì nhận danh sách thay thế.
Adam Robinson

5
@uriz @AdamRobinson kể từ khi chúng ta thảo luận về các giải pháp tao nhã ...list1.ForEach(c => list2.Remove(c));
David Sherret

1
"Thanh lịch" có nghĩa là "nhà phát triển bị mắc kẹt trong việc duy trì mã này sẽ thấy nó đơn giản và dễ hiểu", đó là lý do tại sao đây là câu trả lời tốt nhất.
Seth

22

Tôi khuyên bạn nên sử dụng các phương pháp mở rộng LINQ . Bạn có thể dễ dàng làm điều đó với một dòng mã như vậy:

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

Tất nhiên, điều này là giả sử các đối tượng trong list1 mà bạn đang xóa khỏi list2 là cùng một thể hiện.


2
Nó loại bỏ trùng lặp là tốt.
JulyOrdinary

17

Trong trường hợp của tôi, tôi có hai danh sách khác nhau, với một định danh chung, giống như một khóa ngoại. Giải pháp thứ hai được trích dẫn bởi "nzrytmn" :

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

Là người phù hợp nhất trong tình huống của tôi. Tôi cần tải DropDownList mà không cần các bản ghi đã được đăng ký.

Cảm ơn bạn !!!

Đây là mã của tôi:

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

Tôi không bao giờ giải thích DDlT3 là gì
rogue39nin

14

Bạn có thể sử dụng LINQ, nhưng tôi sẽ sử dụng RemoveAllphương pháp này. Tôi nghĩ rằng đó là một trong những thể hiện tốt hơn ý định của bạn.

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

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

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

8
Hoặc thậm chí đơn giản hơn với các nhóm phương thức bạn có thể làm - số nguyên.Remove ALL (remove.Contains);
Ryan

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

hay còn gọi là "hoàn toàn không trong sạch" :-)
Xan-Kun Clark-Davis

Có chuyện gì với nó vậy. Có vẻ tốt hơn sau đó tạo một danh sách khác bằng cách sử dụng Ngoại trừ. Đặc biệt là khi cả hai danh sách đều rất nhỏ.
Mike Keskinov

1
Vì cả hai phương pháp danh sách là O(N), điều này sẽ dẫn đến O(N^2)đó có thể là một vấn đề với danh sách lớn.
tigrou

7

Giải pháp 1: Bạn có thể làm như thế này:

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

Nhưng trong một số trường hợp có thể giải pháp này không hoạt động. nếu nó không hoạt động bạn có thể sử dụng giải pháp thứ hai của tôi.

Giải pháp 2:

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

chúng tôi giả vờ rằng list1 là danh sách chính của bạn và list2 là danh sách thứ hai của bạn và bạn muốn nhận các mục của list1 mà không có các mục của list2.

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

0

Exceptkhông sửa đổi danh sách, bạn có thể sử dụng ForEach trên List<T>:

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

Nó có thể không phải là cách hiệu quả nhất, nhưng nó đơn giản, do đó có thể đọc được và nó cập nhật danh sách ban đầu (đó là yêu cầu của tôi).


-3

Ở đây ya đi ..

    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. Điều này sẽ ném một ngoại lệ sau khi mục đầu tiên được gỡ bỏ. Ngoài ra, nó (nói chung) là một ý tưởng tốt hơn để duyệt qua danh sách để xóa , vì nó thường nhỏ hơn. Bạn cũng đang buộc nhiều người đi qua danh sách thực hiện theo cách này.
Adam Robinson
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.