ใน LINQ ไม่มี Keyword NOT IN โดยตรง แต่ใช้ Except, Where + !Contains, หรือ !Any แทน
สถานการณ์: ได้ Items ใน List A ที่ไม่อยู่ใน List B
var listA = new[] { 1, 2, 3, 4, 5, 6 };
var listB = new[] { 3, 4 }; // Items to exclude
วิธีที่ 1: Except (Set Operation - แนะนำมากที่สุด)
// คืน Elements ใน listA ที่ไม่อยู่ใน listB (Distinct + Order Preserved from listA)
var result = listA.Except(listB);
foreach (var item in result) Console.WriteLine(item);
// Output: 1, 2, 5, 6
- ใช้
IEqualityComparerได้สำหรับ Custom Comparison - ผลลัพธ์เป็น Distinct (Unique) อัตโนมัติ
วิธีที่ 2: Where + !Contains (Filtering - รองรับ Duplicate)
// รักษา Duplicate ของ listA (ถ้ามี)
var result = listA.Where(x => !listB.Contains(x));
// listA = {1, 2, 3, 3, 4, 5} -> Result: 1, 2, 5 (3, 4 ถูกกรองออก)
- Performance: O(N*M) หาก listB ใหญ่ → ควรแปลง listB เป็น HashSet ก่อน
วิธีที่ 3: Where + !Any (สำหรับ Complex Objects)
var listA = new[]
{
new { Id = 1, Name = "A" },
new { Id = 2, Name = "B" },
new { Id = 3, Name = "C" }
};
var listB = new[] { 2, 3 }; // IDs to exclude
var result = listA.Where(a => !listB.Any(b => b == a.Id));
// Result: { Id=1, Name="A" }
วิธีที่ 4: Join + Where (Left Anti Semi Join)
var result = from a in listA
join b in listB on a equals b into gj
from sub in gj.DefaultIfEmpty()
where sub == 0 // หรือ default(int)
select a;
// ซับซ้อนกว่า Except/Where - ไม่แนะนำสำหรับกรณีง่าย
Optimization: HashSet สำหรับ Contains
// แปลง listB เป็น HashSet ก่อน (O(1) Lookup)
var excludeSet = new HashSet(listB);
var result = listA.Where(x => !excludeSet.Contains(x));
// หรือ
var result = listA.Except(excludeSet); // Except ยอมรับ IEnumerable
ตัวอย่างกับ Object และ Custom Comparer
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
var products = new List
{
new Product { Id = 1, Name = "Apple" },
new Product { Id = 2, Name = "Banana" },
new Product { Id = 3, Name = "Cherry" }
};
var excludedIds = new HashSet { 2 };
// Except ต้องมี IEqualityComparer
var result = products
.Where(p => !excludedIds.Contains(p.Id))
.ToList();
// หรือ Custom Comparer สำหรับ Except
public class ProductIdComparer : IEqualityComparer
{
public bool Equals(Product x, Product y) => x?.Id == y?.Id;
public int GetHashCode(Product obj) => obj.Id.GetHashCode();
}
var excludedProducts = new List { new Product { Id = 2 } };
var result2 = products.Except(excludedProducts, new ProductIdComparer());
SQL-style Syntax (Query Syntax)
var result = from a in listA
where !listB.Contains(a)
select a;
// หรือ
var result = from a in listA
where !(from b in listB select b).Contains(a)
select a;
สรุปเลือกใช้ไหน?
| Scenario | Method |
|---|---|
| Simple Types, Unique Result | Except |
| Preserve Duplicates | Where + !HashSet.Contains |
| Complex Objects, Key-based | Where + !HashSet.Contains(Key) |
| Large Data (Performance) | Pre-build HashSet + Where |