LINQで「ない」クエリをどのように実行しますか?


307

Email両方のコレクションにプロパティを持つ2つのコレクションがあります。Email2番目のリストに存在しない最初のリストの項目のリストを取得する必要があります。SQLでは "not in"を使用するだけですが、LINQで同等のものを知りません。それはどのように行われますか?

これまでのところ、参加しています...

var matches = from item1 in list1
join item2 in list2 on item1.Email equals item2.Email
select new { Email = list1.Email };

しかし、違いが必要なので参加できず、参加できません。私は、ContainsまたはExistsを使用するいくつかの方法が必要だと考えています。まだその例を見つけていません。


3
エコーストームの回答は、ロバートの回答よりもはるかに明確なコードを生成することに注意してください
Nathan Koop

回答:


302

これがあなたに役立つかどうかわかりませんが..

NorthwindDataContext dc = new NorthwindDataContext();    
dc.Log = Console.Out;

var query =    
    from c in dc.Customers    
    where !(from o in dc.Orders    
            select o.CustomerID)    
           .Contains(c.CustomerID)    
    select c;

foreach (var c in query) Console.WriteLine( c );

SQLへのLINQでNOT IN句によって、マルコ・ルッソ


しかし、私はエンティティにlinqを使用しているため、「プリミティブ型のみを使用できます」というエラーが発生します。何か回避策はありますか...?リストを手動で反復して見つけることは別として。
初心者

13
これは、LINQ to Entitiesを使用するとうまくいきます。SQLはWHERE NOT EXISTS(subquery)クエリになります。多分これに対処する更新がありましたか?
scottheckel

2
...そう質問スコープする必要があるかもしれないし、ここに答える..私はEFの新しいバージョンがサポート.Containsを行う考えて、プラスこの質問はタグEF(バージョン)またはLinqToSQLない
ブレット・キャスウェル

4
@Robert Rouse-linq to sqlのThe not in cluseへのリンクが機能しなくなりました。ただのファイ。
JonH 2016

提供されたリンクは、マルウェアを含むとしてフラグが立てられたサイトにつながります。
mikesigs 2018年

334

Except演算子が必要です。

var answer = list1.Except(list2);

ここでより良い説明:https : //docs.microsoft.com/archive/blogs/charlie/linq-farm-more-on-set-operators

注:複雑な型でメソッドを使用するにはIEqualityComparerを実装する必要があるため、この手法はプリミティブ型にのみ最適に機能しますExcept


7
例外の使用:複合型リストを操作する場合は、IEqualityComparer <MyComlplexType>を実装する必要があります。これにより、それほど
うまくいき

4
参照の等価性を比較するだけの場合、またはT.Equals()とT.GetHashCode()をオーバーライドした場合、IEqualityComparer <T>を実装する必要はありません。IEqualityComparer <T>を実装しない場合、EqualityComparer <T> .Defaultが使用されます。
ピエダー2013年

2
@Echostorm(および他の人が読んでいる)、匿名オブジェクトへの選択を行う場合、HashCodeはプロパティ値によって決定されます。list1.Select(item => new { Property1 = item.Property1, Property2 = item.Property2 }).Except(list2.Select( item => new { Property1 = item.Property1, Property2 = item.Property2 }));これは、複合型の値のセットのみを評価して同等性を判断する場合に特に役立ちます。
Brett Caswell 2015年

3
実際、誰かが以下で指摘しましたが、私は正しく、シナリオIEquatityComparor<T,T>でオブジェクト比較メソッドを実装またはオーバーライドする必要はないと思いLinqToSqlます。なぜなら、クエリはSQLとして表される/コンパイルされる/ SQLとして表されるためです。したがって、オブジェクト参照ではなく値がチェックされます。
Brett Caswell 2015年

2
を使用しexceptて、LINQクエリを8〜10秒から0.5秒に高速化できました
Michael Kniskern

61

インメモリオブジェクトのグループから始めて、データベースに対してクエリを実行している人にとって、これが最善の方法であることがわかりました。

var itemIds = inMemoryList.Select(x => x.Id).ToArray();
var otherObjects = context.ItemList.Where(x => !itemIds.Contains(x.Id));

これにより、WHERE ... IN (...)SQLに適切な句が生成されます。


1
実際には、それを3.5で行うことができます
George Silva、

59

電子メールが2番目のリストに存在しない最初のリストのアイテム。

from item1 in List1
where !(list2.Any(item2 => item2.Email == item1.Email))
select item1;

16

WhereとAnyの組み合わせを使用して、以下にないものを検索できます。

var NotInRecord =list1.Where(p => !list2.Any(p2 => p2.Email  == p.Email));

8

両方のコレクションを2つの異なるリスト(list1とlist2など)で取得できます。

次に書くだけ

list1.RemoveAll(Item => list2.Contains(Item));

これは機能します。


3
いいですが、リストから要素を削除するという副作用があります。
Tarik

7

ADO.NET Entity Frameworkを使用している場合、EchoStormのソリューションも完全に機能します。しかし、頭を包むのに数分かかりました。データベースコンテキストdcがあり、テーブルyでリンクされていないテーブルxの行を検索する場合、完全な回答は次のようになります。

var linked =
  from x in dc.X
  from y in dc.Y
  where x.MyProperty == y.MyProperty
  select x;
var notLinked =
  dc.X.Except(linked);

Andyのコメントに応じて、はい、LINQクエリに2つのfromを含めることができます。リストを使用した完全な作業例を以下に示します。FooとBarの各クラスにはIDがあります。Fooには、Foo.BarIdを介したBarへの「外部キー」参照があります。プログラムは、対応するバーにリンクされていないすべてのFooを選択します。

class Program
{
    static void Main(string[] args)
    {
        // Creates some foos
        List<Foo> fooList = new List<Foo>();
        fooList.Add(new Foo { Id = 1, BarId = 11 });
        fooList.Add(new Foo { Id = 2, BarId = 12 });
        fooList.Add(new Foo { Id = 3, BarId = 13 });
        fooList.Add(new Foo { Id = 4, BarId = 14 });
        fooList.Add(new Foo { Id = 5, BarId = -1 });
        fooList.Add(new Foo { Id = 6, BarId = -1 });
        fooList.Add(new Foo { Id = 7, BarId = -1 });

        // Create some bars
        List<Bar> barList = new List<Bar>();
        barList.Add(new Bar { Id = 11 });
        barList.Add(new Bar { Id = 12 });
        barList.Add(new Bar { Id = 13 });
        barList.Add(new Bar { Id = 14 });
        barList.Add(new Bar { Id = 15 });
        barList.Add(new Bar { Id = 16 });
        barList.Add(new Bar { Id = 17 });

        var linked = from foo in fooList
                     from bar in barList
                     where foo.BarId == bar.Id
                     select foo;
        var notLinked = fooList.Except(linked);
        foreach (Foo item in notLinked)
        {
            Console.WriteLine(
                String.Format(
                "Foo.Id: {0} | Bar.Id: {1}",
                item.Id, item.BarId));
        }
        Console.WriteLine("Any key to continue...");
        Console.ReadKey();
    }
}

class Foo
{
    public int Id { get; set; }
    public int BarId { get; set; }
}

class Bar
{
    public int Id { get; set; }
}

LINQで2つのフォームを使用する それは役に立ちます。
アンディ

Andy:はい、上記の改訂された回答を参照してください。
ブレット

4
var secondEmails = (from item in list2
                    select new { Email = item.Email }
                   ).ToList();

var matches = from item in list1
              where !secondEmails.Contains(item.Email)
              select new {Email = item.Email};

4

使用することもできます All()

var notInList = list1.Where(p => list2.All(p2 => p2.Email != p.Email));

2

一方でExceptその答えの一部であり、それは全体の答えではありません。既定では、Except(いくつかのLINQ演算子のように)参照型の参照比較を行います。オブジェクトの値で比較するには、以下を行う必要があります

  • IEquatable<T>あなたのタイプで実装する、または
  • 上書きEqualsしてGetHashCodeあなたのタイプで、または
  • 型に実装さIEqualityComparer<T>れている型のインスタンスを渡す

2
... LINQ to Objectsについて話している場合。LINQ to SQLの場合、クエリはデータベースで実行されるSQLステートメントに変換されるため、これは当てはまりません。
ルーカス

1

簡単にするためにintのリストを使用した例。

List<int> list1 = new List<int>();
// fill data
List<int> list2 = new List<int>();
// fill data

var results = from i in list1
              where !list2.Contains(i)
              select i;

foreach (var result in results)
    Console.WriteLine(result.ToString());

1

INC#でSQLに似た演算子も使用したい場合は、次のパッケージをダウンロードしてください。

Mshwf.NiceLinq

それにはInNotInメソッドがあります:

var result = list1.In(x => x.Email, list2.Select(z => z.Email));

こんな風に使えます

var result = list1.In(x => x.Email, "a@b.com", "b@c.com", "c@d.com");

0

ありがとう、ブレット。あなたの提案は私にも役立ちました。オブジェクトのリストがあり、オブジェクトの別のリストを使用してそれをフィルタリングしたいと思いました。再度、感謝します....

誰かが必要なら、私のコードサンプルを見てください:

'First, get all the items present in the local branch database
Dim _AllItems As List(Of LocalItem) = getAllItemsAtBranch(BranchId, RecordState.All)

'Then get the Item Mappings Present for the branch
Dim _adpt As New gItem_BranchesTableAdapter
Dim dt As New ds_CA_HO.gItem_BranchesDataTable
    _adpt.FillBranchMappings(dt, BranchId)

Dim _MappedItems As List(Of LocalItem) = (From _item As LocalItem In _AllItems Join _
    dr As ds_CA_HO.gItem_BranchesRow In dt _
    On _item.Id Equals dr.numItemID _
    Select _item).ToList

_AllItems = _AllItems.Except(_MappedItems.AsEnumerable).ToList

 Return _AllItems

0

私はこれをLINQ to Entitiesでテストしませんでした:

NorthwindDataContext dc = new NorthwindDataContext();    
dc.Log = Console.Out;

var query =    
    from c in dc.Customers 
    where !dc.Orders.Any(o => o.CustomerID == c.CustomerID)   
    select c;

または:

NorthwindDataContext dc = new NorthwindDataContext();    
dc.Log = Console.Out;

var query =    
    from c in dc.Customers 
    where dc.Orders.All(o => o.CustomerID != c.CustomerID)   
    select c;

foreach (var c in query) 
    Console.WriteLine( c );

0

グループが空の場合、最初のリストから項目を選択するだけで、外部結合を実行できませんか?何かのようなもの:

Dim result = (From a In list1
              Group Join b In list2 
                  On a.Value Equals b.Value 
                  Into grp = Group
              Where Not grp.Any
              Select a)

これがエンティティフレームワークで何らかの効率的な方法で機能するかどうかはわかりません。


0

または、次のようにすることもできます。

var result = list1.Where(p => list2.All(x => x.Id != p.Id));
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.