Jak zrobić zapytanie "Nie w" z LINQ?

Mam dwa zbiory, które mają własność Email w obu zbiorach. Muszę uzyskać listę elementów z pierwszej listy, gdzie Email nie istnieje na drugiej liście. W SQL użyłbym tylko "not in", ale nie znam odpowiednika w LINQ. Jak to się robi?

Jak na razie mam połączenie...
var matches = from item1 in list1
join item2 in list2 on item1.Email equals item2.Email
select new { Email = list1.Email };

Ale nie mogę dołączyć, ponieważ potrzebuję różnicy, a połączenie zawiedzie. Potrzebuję jakiegoś sposobu na użycie zawartości lub istnieje, jak sądzę. Po prostu nie znalazłem przykładu do zrobienia to jeszcze.

 269
Author: Peter Mortensen, 2008-10-08

15 answers

Nie wiem, czy to ci pomoże, ale ... .

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 );

From The NOT in clause in LINQ to SQL by

 262
Author: Robert Rouse,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-26 05:41:48

Chcesz operatora z wyjątkiem.

var answer = list1.Except(list2);

Lepsze wyjaśnienie tutaj: http://blogs.msdn.com/charlie/archive/2008/07/12/the-linq-set-operators.aspx

Uwaga: ta technika działa najlepiej tylko dla typów prymitywnych, ponieważ musisz zaimplementować IEqualityComparer aby użyć metody Except z typami złożonymi.

 299
Author: Echostorm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-19 16:42:33

Pozycje na pierwszej liście, gdzie e-mail nie istnieje na drugiej liście.

from item1 in List1
where !(list2.Any(item2 => item2.Email == item1.Email))
select item1;
 57
Author: Amy B,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-08 17:50:41

Dla osób, które zaczynają od grupy obiektów w pamięci i pytają o bazę danych, uznałem to za najlepszy sposób:

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

To tworzy ładną klauzulę WHERE ... IN (...) w SQL.

 54
Author: StriplingWarrior,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-05 15:21:42

Możesz użyć kombinacji Where I Any dla znalezienia Nie w:

var NotInRecord =list1.Where(p => !list2.Any(p2 => p2.Email  == p.Email));
 11
Author: DevT,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-06 20:33:05

Możesz wziąć oba zbiory w dwóch różnych listach, powiedzmy list1 i list2.

To po prostu napisz

list1.RemoveAll(Item => list2.Contains(Item));
To zadziała.
 8
Author: Chintan Udeshi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-06 20:29:58

W przypadku, gdy ktoś używa ADO.NET Entity Framework , rozwiązanie Echostorma również działa doskonale. Ale zajęło mi kilka minut, żeby to ogarnąć. Zakładając, że masz kontekst bazy danych, dc i chcesz znaleźć wiersze w tabeli x, które nie są połączone w tabeli y, pełna odpowiedź odpowiedzi wygląda następująco:

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);

W odpowiedzi na komentarz Andy 'ego, tak, można mieć dwa from' y w zapytaniu LINQ. Oto kompletny przykład pracy, używając list. Każda klasa, Foo i Bar, ma identyfikator. Foo ma" klucz obcy " odniesienie do paska przez Foo.BarId. Program wybiera wszystkie Foo Nie powiązane z odpowiednim paskiem.

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; }
}
 7
Author: Brett,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-06 20:27:40
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};
 3
Author: tvanfosson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-08 17:05:20

Podczas gdy Except jest częścią odpowiedzi, nie jest to cała odpowiedź. Domyślnie, Except (podobnie jak kilka operatorów LINQ) dokonuje porównania referencji na typach referencji. Aby porównać wartości w obiektach, musisz

  • zaimplementuj IEquatable<T> w swoim typie, lub
  • override Equals and GetHashCode in your type, or
  • pass in a instance of a type implementing IEqualityComparer<T> for your type
 2
Author: Ryan Lundy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-08 18:47:10

Przykład użycia listy int dla uproszczenia.

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
Author: Inisheer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-08 17:08:13

Dla każdego, kto również chce używać operatora SQL-alike IN w C#, Pobierz ten pakiet:

Mshwf.NiceLinq

Ma metody In i NotIn:

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

Nawet ty możesz użyć tego w ten sposób

var result = list1.In(x => x.Email, "[email protected]", "[email protected]", "[email protected]");
 1
Author: mshwf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-12 12:13:43

Można też użyć All()

var notInList = list1.Where(p => list2.All(p2 => p2.Email != p.Email));
 1
Author: Janis S.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-28 08:52:00

Dziękuję, Brett. Twoja sugestia też mi pomogła. Miałem listę obiektów i chciałem filtrować ją za pomocą innej listy obiektów. Jeszcze raz dziękuję....

Jeśli ktoś potrzebuje, proszę spojrzeć na mój kod przykładowy:

'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
Author: mangeshkt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-06 20:28:49

Nie testowałem tego z 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;

Alternatywnie:

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
Author: Tarik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-06 20:32:13

Nie możesz wykonać zewnętrznego połączenia, wybierając tylko elementy z pierwszej listy, jeśli grupa jest pusta? Coś w stylu:

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)

Nie jestem pewien, czy to zadziała w jakikolwiek efektywny sposób z ramą encji.

 0
Author: Marten Jacobs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-23 11:33:09