CodeGym /Cours /C# SELF /Opérations Union, Intersect, Except dans LINQ

Opérations Union, Intersect, Except dans LINQ

C# SELF
Niveau 33, Leçon 3
Disponible

1. Introduction

On passe à un sujet super cool et vraiment utile en pratique : les opérations d’ensemble LINQ — Union, Intersect, Except. Avec ça, tu bosses avec des collections comme si tu faisais de l’algèbre des ensembles (ou que tu comptes les stickers dans deux paquets sur le bureau d’un dev — avoue, c’est presque pareil). Si t’as jamais entendu parler d’algèbre des ensembles (ou que t’as zappé ça), ça peut faire flipper. Mais en vrai, c’est super simple : imagine deux sacs de fruits. L’algèbre des ensembles, c’est juste une façon de piger quels fruits sont dans les deux sacs, lesquels sont que dans un seul, et lesquels tu as si tu mélanges tout. C’est comme jouer avec des autocollants : tu peux tout mettre ensemble, trouver ceux en commun ou enlever certains d’un autre. Voilà, c’est tout.

Pratique : Ces méthodes sont super pratiques quand tu veux fusionner les résultats de deux requêtes différentes, trouver les éléments communs ou voir la différence entre deux collections. Par exemple :

  • Faire une liste générale de tous les produits qui sont soit dans la liste des ventes, soit dans la liste des achats.
  • Trouver les produits qui sont dans les deux — les positions communes.
  • Ou voir quels produits sont en stock mais n’ont jamais été achetés — y’en a qui prennent la poussière !

Cas réels : L’algèbre des ensembles est partout : filtrer des users par abonnements, trouver ceux qui étaient dans plusieurs groupes, chercher des commandes uniques ou croisées, comparer les résultats de deux requêtes SQL différentes, et plein d’autres trucs.

2. Opération Union — fusionner des collections

C’est easy, alors on passe direct à la pratique. Imaginons qu’on a deux listes de produits :

List<string> produitsEntrepot = new List<string> { "Lait", "Pain", "Fromage", "Oeufs" };
List<string> recemmentVendus = new List<string> { "Pain", "Fromage", "Saucisson", "Thé" };

Qu’est-ce que fait Union ?

Union te renvoie les éléments uniques qui sont au moins dans une des collections.
En gros, c’est la fusion.

var tousLesProduits = produitsEntrepot.Union(recemmentVendus);

foreach (var produit in tousLesProduits)
{
    Console.WriteLine(produit);
}
// Affichera : Lait, Pain, Fromage, Oeufs, Saucisson, Thé

Visuellement, ça donne ça :

Entrepôt Vendu Union (Fusion)
Lait Pain Lait
Pain Fromage Pain
Fromage Saucisson Fromage
Oeufs Thé Oeufs
Saucisson
Thé

En mode algèbre des ensembles, Union c’est l’opération "ou" : donne-moi tout ce qu’il y a quelque part. Imagine deux boîtes avec des thés différents — si tu veux goûter tous les goûts, même si un thé est dans les deux, tu le bois qu’une fois.

Union supprime automatiquement les doublons (en se basant sur l’implémentation de Equals et GetHashCode pour le type d’éléments). Si tu fais la fusion pour tes propres classes (genre Product), assure-toi que ces méthodes sont bien codées, sinon Union va faire n’importe quoi : les mêmes éléments peuvent être vus comme différents !

À retenir : l’ordre des éléments dans la collection résultat garde l’ordre de la première collection, et les nouveaux sont ajoutés à la fin (dans l’ordre où ils apparaissent dans la deuxième collection).

Exemple avec des objets

On continue notre appli "Magasin". On a deux listes de Product :

public class Product
{
    public string Name { get; set; }
    public string Category { get; set; }

    // Pour que Union/Intersect/Except marche bien, il faut un bon Equals et GetHashCode !
    public override bool Equals(object? obj) =>
        obj is Product other && Name == other.Name && Category == other.Category;

    public override int GetHashCode() => HashCode.Combine(Name, Category);
}

List<Product> stock = new()
{
    new Product { Name = "Lait", Category = "Laitiers" },
    new Product { Name = "Pain", Category = "Boulangerie" },
};

List<Product> vendu = new()
{
    new Product { Name = "Pain", Category = "Boulangerie" },
    new Product { Name = "Saucisson", Category = "Charcuterie" },
};

var tous = stock.Union(vendu);

// Attention : pas de doublons,
// même si ça "semble" pareil, mais ça doit être égal selon la logique.

foreach (var p in tous)
    Console.WriteLine($"{p.Name} ({p.Category})");

Erreur classique : Si tu ne redéfinis pas Equals/GetHashCode, Union va considérer différents objets avec les mêmes valeurs comme différents éléments !

3. Opération Intersect — intersection des collections

Intersect te renvoie les éléments qui sont dans toutes les collections d’origine. C’est un peu le "et" de l’algèbre des ensembles.

Exemple

On reprend les listes du début :

var produitsCommuns = produitsEntrepot.Intersect(recemmentVendus);
foreach (var produit in produitsCommuns)
{
    Console.WriteLine(produit);
}
// Affichera : Pain, Fromage

Visuel :

Entrepôt Vendu Intersect (Commun)
Pain Pain Pain
Fromage Fromage Fromage

À quoi ça sert ?

  • Tu veux savoir quels produits de ton stock ont été vendus aujourd’hui.
  • En entretien, on te demande souvent : "Comment trouver l’intersection de deux listes ?" — LINQ fait ça en une ligne !
  • Dans les applis business, l’intersection sert souvent à filtrer selon plusieurs critères croisés.

Particularités et erreurs fréquentes

Si un élément est plusieurs fois dans la collection, il n’apparaîtra qu’une seule fois dans le résultat.
Pour les objets complexes (genre la classe Product), il faut encore une bonne implémentation de Equals/GetHashCode.

Exemple avec des objets

var objetsCommuns = stock.Intersect(vendu);
foreach (var p in objetsCommuns)
    Console.WriteLine($"{p.Name} ({p.Category})");
// Affichera juste Pain (Boulangerie)

4. Opération Except — différence des collections

Except te renvoie les éléments qui sont seulement dans la première collection, mais pas dans la deuxième.

Exemple

var produitsSeulementStock = produitsEntrepot.Except(recemmentVendus);
foreach (var produit in produitsSeulementStock)
{
    Console.WriteLine(produit);
}
// Affichera : Lait, Oeufs

Donc, ce sont les produits en stock, mais pas vendus.

Visuel :

Entrepôt Vendu Except (seulement stock)
Lait Lait
Oeufs Oeufs
Pain Pain (sauté)
Fromage Fromage (sauté)

Analogie

C’est comme si tu enlevais de ta pile tous les docs déjà envoyés — il ne reste que ce qui est encore chez toi et nulle part ailleurs.

Exemple avec des objets

var invendus = stock.Except(vendu);
foreach (var p in invendus)
    Console.WriteLine($"{p.Name} ({p.Category})");
// Affichera : Lait (Laitiers)

! Points pas évidents

La méthode Except est sensible à l’ordre : A.Except(B) — ce n’est pas pareil que B.Except(A) ! La première collection — "d’où on enlève", la deuxième — "ce qu’on enlève".

5. Combiner et composer plusieurs opérations avec LINQ

Parfois, une seule opération ne suffit pas. Genre, tu veux afficher les produits qui sont soit seulement en stock, soit seulement vendus — mais pas dans les deux à la fois (ce qu’on appelle la "différence symétrique").

Différence symétrique ("XOR" pour les ensembles) :

var seulementDansUn = produitsEntrepot.Except(recemmentVendus)
                   .Union(recemmentVendus.Except(produitsEntrepot));
foreach (var produit in seulementDansUn)
{
    Console.WriteLine(produit);
}
// Affichera : Lait, Oeufs, Saucisson, Thé

Pour des logiques plus complexes, tu peux combiner les méthodes LINQ :

// Trouver les produits qui ne sont ni en stock, ni vendus, mais seulement dans la liste "attendus"
List<string> attendus = new() { "Café", "Thé", "Lait" };
var seulementAttendus = attendus.Except(produitsEntrepot.Union(recemmentVendus));
foreach (var produit in seulementAttendus)
    Console.WriteLine(produit);
// Affichera : Café

6. Travailler avec des types custom et IEqualityComparer

Parfois, tu veux pas comparer les objets sur tous les champs : genre, seul le nom du produit t’intéresse, la catégorie on s’en fout. Pour ça, les méthodes LINQ acceptent un paramètre en plus — IEqualityComparer<T>, qui définit comment comparer les éléments.

Exemple de comparateur perso :

class ProductNameComparer : IEqualityComparer<Product>
{
    public bool Equals(Product? x, Product? y) => x?.Name == y?.Name;
    public int GetHashCode(Product obj) => obj.Name.GetHashCode();
}

var comp = new ProductNameComparer();
var uniqueParNom = stock.Union(vendu, comp);
foreach (var p in uniqueParNom)
    Console.WriteLine(p.Name); // Lait, Pain, Saucisson

C’est super pratique si tu veux pas toucher à Equals/GetHashCode dans tout ton modèle, mais juste comparer les objets selon une règle spécifique.

7. Schémas visuels et tableaux

On résume les usages (pour les listes A et B) :

Opération Résultat
A.Union(B)
Tout ce qu’il y a dans A ou B (éléments uniques).
A.Intersect(B)
Tout ce qu’il y a dans les deux (A et B).
A.Except(B)
Tout ce qu’il y a seulement dans A, mais pas dans B.

Schéma (Diagramme de Venn, si on pouvait dessiner) :


      [A]        [B]
      oooooooooo
      oooooooo oooo
      ooooo oo   ooo
      ooo    ooo  oo
      oo      o  ooo
      ooo    ooo ooo
  • Union : tout ce qui est dans les deux cercles.
  • Intersect : juste l’intersection (le centre).
  • Except : seulement ce qui est dans le cercle A, sans toucher à l’intersection avec B.

8. Erreurs fréquentes avec Union, Intersect et Except

Erreur n°1 : utiliser avec des objets sans Equals et GetHashCode.
Si ta classe n’a pas ces méthodes redéfinies, les méthodes Union, Intersect et Except vont mal marcher : des objets identiques en contenu seront vus comme différents. Résultat : tu vas avoir un résultat chelou (et souvent inutile).

Erreur n°2 : essayer de comparer des objets sur une partie des champs sans IEqualityComparer.
Genre, si tu veux comparer les produits juste par nom, pas par toute la structure, Intersect ne va pas piger. Sans un IEqualityComparer explicite, le résultat ne sera pas celui attendu.

Erreur n°3 : croire que l’ordre des éléments est garanti.
Beaucoup pensent que la collection finale garde l’ordre de la fusion ou de l’intersection. Mais ça dépend de la méthode : Union garde l’ordre de la première collection, mais Intersect et Except peuvent rendre les éléments dans un ordre imprévisible. Mieux vaut ne pas compter dessus.

Erreur n°4 : zapper la perf sur les grosses collections.
Si t’as beaucoup de données, ces méthodes peuvent ramer. Pense à faire de l’agrégation, du filtrage ou à utiliser des structures de hash (genre HashSet) pour accélérer les opérations.

2
Mission
C# SELF, niveau 33, leçon 3
Bloqué
Utilisation de Intersect pour trouver les éléments communs
Utilisation de Intersect pour trouver les éléments communs
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION