Distinct par un seul champ en C# : comment supprimer les doublons d'objets

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Problème : Distinct() ne sait pas filtrer par champ

\n\n

Imaginez : vous avez une liste de clients, et des doublons par email se sont glissés dedans. Vous écrivez :

\n\n
var uniqueClients = clients.Distinct().ToList();
\n\n

Et vous découvrez avec surprise que les doublons n'ont pas disparu. Pourquoi ? Parce que le Distinct() standard compare les objets entiers par référence (ou par Equals, si vous l'avez redéfini). Il ne sait pas que pour vous, « l'unicité » signifie un Email unique, et non une instance de classe unique.

\n\n

Cette question est l'une des plus populaires sur Stack Overflow concernant LINQ (plus de 500 000 vues). Et aujourd'hui, nous allons voir 3 méthodes efficaces pour résoudre ce problème en C#. C'est parti !

\n\n

Méthode 1. GroupBy + First (le classique)

\n\n

La méthode la plus simple et la plus compréhensible, qui ne nécessite pas l'installation de bibliothèques externes. L'idée : regrouper les objets par la propriété souhaitée, puis prendre le premier élément de chaque groupe.

\n\n
var uniqueByEmail = clients\n    .GroupBy(c => c.Email)\n    .Select(g => g.First())\n    .ToList();
\n\n

Comment cela fonctionne :

\n
    \n
  • GroupBy(c => c.Email) — divise la collection en groupes dont la clé est l'email.
  • \n
  • Select(g => g.First()) — prend le premier objet de chaque groupe.
  • \n
  • Les autres doublons sont écartés.
  • \n
\n\n

Avantages :

\n
    \n
  • Fonctionne « prêt à l'emploi » (uniquement using System.Linq;).
  • \n
  • Intuitif même pour un débutant.
  • \n
  • Conserve l'ordre original des éléments (le premier objet rencontré reste).
  • \n
\n\n

Inconvénients :

\n
    \n
  • Légèrement plus lent sur de grandes données (nécessite la création de groupes).
  • \n
  • Code un peu plus verbeux que souhaité.
  • \n
\n\n

Méthode 2. MoreLinq et DistinctBy (élégance)

\n\n

Si vous n'avez pas peur des bibliothèques externes, MoreLinq propose la méthode idéale DistinctBy. Elle fait exactement ce que vous attendez de Distinct — filtrer par propriété.

\n\n

Installation via NuGet :

\n
Install-Package MoreLinq
\n\n

Utilisation :

\n
using MoreLinq;\n\nvar uniqueByEmail = clients.DistinctBy(c => c.Email).ToList();
\n\n

Une seule ligne et tout est prêt. En interne, DistinctBy utilise un HashSet et fonctionne en O(n).

\n\n

Avantages :

\n
    \n
  • Code extrêmement concis et lisible.
  • \n
  • Haute performance.
  • \n
  • Bibliothèque bien testée (plus de 10 millions de téléchargements).
  • \n
\n\n

Inconvénients :

\n
    \n
  • Nécessite d'ajouter une dépendance au projet.
  • \n
  • Toutes les équipes n'autorisent pas les bibliothèques externes.
  • \n
\n\n

Méthode 3. IEqualityComparer personnalisé (contrôle maximal)

\n\n

Cette méthode convient lorsque vous ne voulez pas ajouter de bibliothèque, mais que vous souhaitez réutiliser la logique de comparaison à plusieurs endroits. Créez votre propre comparateur :

\n\n
public class ClientEmailComparer : IEqualityComparer<Client>\n{\n    public bool Equals(Client x, Client y)\n    {\n        if (ReferenceEquals(x, y)) return true;\n        if (x is null || y is null) return false;\n        return x.Email == y.Email;\n    }\n\n    public int GetHashCode(Client obj)\n    {\n        return obj.Email?.GetHashCode() ?? 0;\n    }\n}
\n\n

Utilisation :

\n
var comparer = new ClientEmailComparer();\nvar uniqueByEmail = clients.Distinct(comparer).ToList();
\n\n

Avantages :

\n
    \n
  • Contrôle total sur la logique de comparaison.
  • \n
  • Le comparateur peut être réutilisé dans Contains, Except, Union, etc.
  • \n
  • Ne nécessite pas de bibliothèques externes.
  • \n
\n\n

Inconvénients :

\n
    \n
  • Beaucoup de code passe-partout.
  • \n

Blogs

Book Recommendations