LINQ筆記:使用LINQ ANY達成NOT IN的效果(黑名單)

今天遇到一個LINQ的應用,需要使用到SQL的Where NOT IN某個清單的效果,稍微想了一下把程式碼寫出並記錄在此供回憶。

利用LINQ ANY()達成SQL的NOT IN效果

假設我們有一個oUsers清單,另外有一張oBlocks黑名單,想要將oUsers NOT IN oBlocks,挑選成為一個oAlllows可登入系統清單,程式碼如下:

public static void Main()
{ //使用者清單
  var oUsers = new System.Collections.Generic.List<string>() { "aaa", "bbb", "ccc", "ddd", "eee", "fff" };
  //黑名單
  var oBlocks = new System.Collections.Generic.List<string>() { "bbb", "ddd", "eee" };
  //可登入系統清單
  var oAllows = oUsers.Where(x => !oBlocks.Any(y => y.Contains(x)));
  //輸出
  WriteLine(string.Join("、", oAllows));
  Read();
}

經過這樣的程式碼後,我們就可以將oBlocks清單裡面的成員排除掉了,另外建議若是比對資料量很大的情況下,建議用Except(),輸出的結果如下:

aaa、ccc、fff

利用LINQ ANY()尋找是否有在黑名單裡

有時候我們需要的並不是合法的清冊,而是有出現一筆不合法的都不可以。舉例來說,一個旅行團有10個人,裡面出現一個危險人物可能整團都會被海關帶去問話。以下是範例:

//旅行團清單
var oUsers = new System.Collections.Generic.List<string>() { "王小明", "李小華", "陳小英", "JOHNNY" };
//海關黑名單
var oBlocks = new System.Collections.Generic.List<string>() { "陳大英", "johnny" };
//是否有違法人員
var bIllegal = oUsers.Any(x => oBlocks.Contains(x, System.StringComparer.OrdinalIgnoreCase));
//輸出
Console.WriteLine($"團隊裡面是否有違法人員混入:{bIllegal}");

從上面的程式碼可以發現我故意引入忽略大小寫的判斷,輸出如下:

團隊裡面是否有違法人員混入:True

補充說明:LINQ ALL()

ANY()的運算是「只要有一筆符合」即可(或;OR),反過來的指令ALL()是「所有的資料都要符合」才可(及;AND)。以下用一個簡單的例子來展示補充說明:

public class People
{
  public string cName { get; set; }
  public List oAccountBalance { get; set; }
}
public static void Main()
{
  var oUsers = new System.Collections.Generic.List() {
    new People { cName = "王小明", oAccountBalance = new List() { 123, 456 }},
    new People { cName = "王大頭", oAccountBalance = new List() { 789, 999 }},
    new People { cName = "李小華", oAccountBalance = new List() { 444, 666 }},
  };
  //找出姓名是王開頭且銀行帳戶餘額都超過500
  var oResult = oUsers.Where(x => x.cName.StartsWith("王") && x.oAccountBalance.All(y => y > 500)).Select(x => x.cName);
  //輸出
  WriteLine(string.Join("、", oResult));
  Read();
}

以上面的程式碼為例,最終只有「王大頭」會被挑出,因為他的帳戶餘額789、999都超過500元。如果把條件改成「y > 100」則結果會變成「王小明、王大頭」。

ALL()條件會被誤用通常發生在子集合資料裡面出現字串的時候,例如我們把銀行帳號(數值格式)改成電話號碼(字串格式),我們想要找出姓王且電話中有1234門號的資料,這時候應該要使用ANY()。如果剛好當下頭昏腦脹誤用ALL()並錯誤解釋行為,那可能會讓輸出結果錯亂。(除非某人有兩隻一模一樣的電話號碼,但這在現實中不可能且應該被檢查資料重複性)

相關參考

CSharp LINQ SQL WhereNotIn BlockList BlackList