web-dev-qa-db-fra.com

Comment feriez-vous une requête "pas à" avec LINQ?

J'ai deux collections qui ont la propriété Email dans les deux collections. J'ai besoin d'obtenir une liste des éléments de la première liste où Email n'existe pas dans la deuxième liste. Avec SQL, je voudrais simplement utiliser "not in", mais je ne connais pas l’équivalent dans LINQ. Comment est-ce fait?

Jusqu'à présent, j'ai une jointure, comme ...

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

Mais je ne peux pas rejoindre car j'ai besoin de la différence et la jointure échouerait. J'ai besoin d'un moyen d'utiliser Contient or Exists, je crois. Je n'ai tout simplement pas trouvé d'exemple pour le faire.

286
Brennan

Je ne sais pas si cela vous aidera mais ..

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

à partir de la clause NOT IN dans LINQ to SQL par Marco Russo

282
Robert Rouse

Vous voulez l'opérateur Sauf.

var answer = list1.Except(list2);

Meilleure explication ici: http://blogs.msdn.com/charlie/archive/2008/07/12/the-linq-set-operators.aspx

REMARQUE: Cette technique fonctionne mieux pour les types primitifs uniquement, car vous devez implémenter un IEqualityComparer pour utiliser la méthode Except avec des méthodes complexes. les types.

318
Echostorm

Pour les personnes qui commencent avec un groupe d'objets en mémoire et interrogent une base de données, j'ai trouvé que c'était la meilleure solution:

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

Ceci produit une clause Nice WHERE ... IN (...) en SQL.

58
StriplingWarrior

éléments de la première liste où l’email n’existe pas dans la deuxième liste.

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

Vous pouvez utiliser une combinaison de Où et N'importe qui pour ne pas trouver dans:

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

Vous pouvez prendre les deux collections dans deux listes différentes, par exemple list1 et list2.

Ensuite, écrivez

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

Cela fonctionnera.

8
Chintan Udeshi

Dans le cas où on utilise le ADO.NET Entity Framework , la solution d'EchoStorm fonctionne également parfaitement. Mais il m'a fallu quelques minutes pour envelopper ma tête. En supposant que vous ayez un contexte de base de données, dc, et que vous souhaitiez trouver des lignes dans la table x non liées dans la table y, la réponse complète ressemble à ceci:

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

En réponse au commentaire de Andy, oui, on peut en avoir deux dans une requête LINQ. Voici un exemple de travail complet, en utilisant des listes. Chaque classe, Foo et Bar, a un identifiant. Foo a une référence "clé étrangère" à Bar via Foo.BarId. Le programme sélectionne tous les Foo non liés à une barre correspondante.

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
Brett
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
tvanfosson

On pourrait aussi utiliser All()

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

Alors que Except fait partie de la réponse, ce n'est pas toute la réponse. Par défaut, Except (comme plusieurs opérateurs LINQ) effectue une comparaison de référence sur les types de référence. Pour comparer par les valeurs dans les objets, vous devrez

  • implémenter IEquatable<T> dans votre type, ou
  • remplacer Equals et GetHashCode dans votre type, ou
  • transmettez une instance d'un type implémentant IEqualityComparer<T> pour votre type
2
Ryan Lundy

Exemple utilisant List of int pour plus de simplicité.

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
Inisheer

Pour tous ceux qui souhaitent également utiliser un opérateur IN similaire à SQL en C #, téléchargez ce package:

Mshwf.NiceLinq

Il a In et NotIn méthodes:

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

Même vous pouvez l'utiliser de cette façon

var result = list1.In(x => x.Email, "[email protected]", "[email protected]", "[email protected]");
1
mshwf

Je n'ai pas testé cela avec 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;

Alternativement:

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
Tarik

Merci Brett. Votre suggestion m'a aidé aussi. J'avais une liste d'objets et je voulais filtrer celle-ci en utilisant une autre liste d'objets. Merci encore....

Si quelqu'un en a besoin, veuillez consulter mon exemple de code:

'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
mangeshkt

Ne pourriez-vous pas faire une jointure externe en sélectionnant uniquement les éléments de la première liste si le groupe est vide? Quelque chose comme:

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)

Je ne sais pas si cela fonctionnerait de manière efficace avec le cadre Entity.

0
Marten Jacobs