Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> C#

วิธีใช้ NOT IN Query ใน C# LINQ

ใน 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