2010-10-15 24 views
338

Tôi giả sử có một truy vấn LINQ đơn giản để thực hiện việc này, tôi chỉ không chắc chắn chính xác như thế nào. Vui lòng xem đoạn mã bên dưới.Sử dụng LINQ để lấy các mục trong một Danh sách <>, không có trong Danh sách khác <>

class Program 
{ 
    static void Main(string[] args) 
    { 
     List<Person> peopleList1 = new List<Person>(); 
     peopleList1.Add(new Person() { ID = 1 }); 
     peopleList1.Add(new Person() { ID = 2 }); 
     peopleList1.Add(new Person() { ID = 3 }); 

     List<Person> peopleList2 = new List<Person>(); 
     peopleList2.Add(new Person() { ID = 1 }); 
     peopleList2.Add(new Person() { ID = 2 }); 
     peopleList2.Add(new Person() { ID = 3 }); 
     peopleList2.Add(new Person() { ID = 4 }); 
     peopleList2.Add(new Person() { ID = 5 }); 
    } 
} 

class Person 
{ 
    public int ID { get; set; } 
} 

Tôi muốn thực hiện một truy vấn LINQ to cho tôi tất cả những người trong peopleList2 mà không phải là trong peopleList1 ví dụ này nên cung cấp cho tôi hai người (ID = 4 & ID = 5)

+2

Có lẽ đó là một tốt ý tưởng để làm cho ID chỉ đọc vì danh tính của một đối tượng không nên thay đổi theo thời gian sống của nó. Trừ khi khóa học của bạn hoặc khung công tác ORM yêu cầu nó phải có thể thay đổi được. – CodesInChaos

+0

Chúng tôi có thể gọi đây là "Trái (hoặc Phải) Không Tham gia" theo [sơ đồ này?] (Https://www.codeproject.com/Articles/33052/Visual-Representation-of-SQL-Joins) –

Trả lời

615
var result = peopleList2.Where(p => !peopleList1.Any(p2 => p2.ID == p.ID)); 
+1

@JSprang , tuyệt, bạn được chào đón. Bạn nên đánh dấu câu trả lời của tôi là * câu trả lời * nếu nó giúp bạn (bằng cách nhấp vào dấu kiểm bên trái), cách này người khác có thể thấy rằng đây là câu trả lời chính xác (và tôi nhận được nhiều danh tiếng hơn ;-) –

+26

Bạn biết rằng đó là một giải pháp O (n * m) cho một vấn đề có thể dễ dàng được giải quyết trong thời gian O (n + m)? – Niki

+0

Vâng, nó sẽ không cho phép tôi đánh dấu nó như là câu trả lời ngay lập tức, nói rằng tôi cần phải chờ 5 phút :) Cảm ơn một lần nữa! – JSprang

260

Nếu bạn ghi đè lên sự bình đẳng của dân sau đó bạn cũng có thể sử dụng:

peopleList2.Except(peopleList1) 

Except nên nhanh hơn đáng kể biến thể Where(...Any) vì nó có thể đặt danh sách thứ hai thành một hashtable. Where(...Any) có thời gian chạy là O(peopleList1.Count * peopleList2.Count) trong khi các biến thể dựa trên HashSet<T> (gần như) có thời gian chạy là O(peopleList1.Count + peopleList2.Count).

Except loại bỏ hoàn toàn các bản sao. Điều đó không ảnh hưởng đến trường hợp của bạn, nhưng có thể là một vấn đề đối với các trường hợp tương tự.

Hoặc nếu bạn muốn mã nhanh nhưng không muốn ghi đè lên sự bình đẳng:

var excludedIDs = new HashSet<int>(peopleList1.Select(p => p.ID)); 
var result = peopleList2.Where(p => !excludedIDs.Contains(p.ID)); 

biến thể này không loại bỏ bản sao.

+0

Điều đó sẽ chỉ hoạt động nếu 'Equals' bị ghi đè để so sánh ID. –

+0

Đúng @klausbyskov, chỉ cần thử điều này và tôi nhận được 5 kết quả. – JSprang

+16

Đó là lý do tại sao tôi viết rằng bạn cần phải ghi đè lên sự bình đẳng. Nhưng tôi đã thêm một ví dụ hoạt động ngay cả khi không có điều đó. – CodesInChaos

28

Vì tất cả những giải pháp cho đến nay sử dụng cú pháp thông thạo, đây là một giải pháp trong cú pháp biểu thức truy vấn, cho những người quan tâm:

var peopleDifference = 
    from person2 in peopleList2 
    where !(
     from person1 in peopleList1 
     select person1.ID 
    ).Contains(person2.ID) 
    select person2; 

Tôi nghĩ rằng đó là khác nhau đủ từ các câu trả lời dành cho thể quan tâm một số, thậm chí nghĩ rằng nó rất có thể sẽ được tối ưu hóa cho danh sách. Bây giờ cho các bảng có ID được lập chỉ mục, điều này chắc chắn sẽ là cách để đi.

0

Dưới đây là ví dụ làm việc để có được các kỹ năng CNTT mà ứng viên công việc không đã có.

//Get a list of skills from the Skill table 
IEnumerable<Skill> skillenum = skillrepository.Skill; 
//Get a list of skills the candidate has     
IEnumerable<CandSkill> candskillenum = candskillrepository.CandSkill 
     .Where(p => p.Candidate_ID == Candidate_ID);     
//Using the enum lists with LINQ filter out the skills not in the candidate skill list 
IEnumerable<Skill> skillenumresult = skillenum.Where(p => !candskillenum.Any(p2 => p2.Skill_ID == p.Skill_ID)); 
//Assign the selectable list to a viewBag 
ViewBag.SelSkills = new SelectList(skillenumresult, "Skill_ID", "Skill_Name", 1); 
36

Hoặc nếu bạn muốn nó không phủ định:

var result = peopleList2.Where(p => peopleList1.All(p2 => p2.ID != p.ID)); 

Về cơ bản nó nói có được tất cả từ peopleList2 nơi tất cả các id trong peopleList1 khác với id trong peoplesList2.

Chỉ cần một cách tiếp cận một chút khác biệt so với câu trả lời chấp nhận :)

+2

Phương pháp này (danh sách hơn 50.000 mục) nhanh hơn đáng kể so với phương thức BẤT CỨ! – DaveN

10

Bit muộn để dự buổi tiệc nhưng một giải pháp tốt mà cũng là LINQ to SQL tương thích là:

List<string> list1 = new List<string>() { "1", "2", "3" }; 
List<string> list2 = new List<string>() { "2", "4" }; 

List<string> inList1ButNotList2 = (from o in list1 
            join p in list2 on o equals p into t 
            from od in t.DefaultIfEmpty() 
            where od == null 
            select o).ToList<string>(); 

List<string> inList2ButNotList1 = (from o in list2 
            join p in list1 on o equals p into t 
            from od in t.DefaultIfEmpty() 
            where od == null 
            select o).ToList<string>(); 

List<string> inBoth = (from o in list1 
         join p in list2 on o equals p into t 
         from od in t.DefaultIfEmpty() 
         where od != null 
         select od).ToList<string>(); 

Kudos để http://www.dotnet-tricks.com/Tutorial/linq/UXPF181012-SQL-Joins-with-C

4

Tiện ích mở rộng đầy đủ này cho phép bạn xác định danh sách mục cần loại trừ và chức năng sử dụng để tìm khóa để sử dụng để thực hiện so sánh.

public static class EnumerableExtensions 
{ 
    public static IEnumerable<TSource> Exclude<TSource, TKey>(this IEnumerable<TSource> source, 
    IEnumerable<TSource> exclude, Func<TSource, TKey> keySelector) 
    { 
     var excludedSet = new HashSet<TKey>(exclude.Select(keySelector)); 
     return source.Where(item => !excludedSet.Contains(keySelector(item))); 
    } 
} 

Bạn có thể sử dụng nó theo cách này

list1.Exclude(list2, i => i.ID); 
2

Klaus' Câu trả lời là tuyệt vời, nhưng ReSharper sẽ yêu cầu bạn "Đơn giản hóa biểu thức LINQ":

var result = peopleList2.Where(p => peopleList1.All(p2 => p2.ID != p.ID));

Các vấn đề liên quan