LINQ
Programming
.NET
C#
Distinct() Method

LINQ's Distinct() on a particular property

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Distinct() removes duplicate elements based on the equality of the element type itself. When you want uniqueness based on only one property, the cleanest solution depends on your .NET version: modern code usually uses DistinctBy, while older code falls back to GroupBy or a custom comparer.

Why Plain Distinct() Is Not Enough

Given a class like this:

csharp
1public class Employee
2{
3    public int Id { get; set; }
4    public string Name { get; set; } = "";
5}

calling Distinct() on a list of Employee objects does not magically know that Id is the property you care about.

csharp
1var employees = new List<Employee>
2{
3    new Employee { Id = 1, Name = "Alice" },
4    new Employee { Id = 2, Name = "Bob" },
5    new Employee { Id = 1, Name = "Alicia" }
6};
7
8var result = employees.Distinct().ToList();

Unless Employee overrides equality appropriately, those objects are treated as different instances.

Modern Solution: DistinctBy

If you are on modern .NET, DistinctBy is the most direct answer.

csharp
1using System;
2using System.Collections.Generic;
3using System.Linq;
4
5var employees = new List<Employee>
6{
7    new Employee { Id = 1, Name = "Alice" },
8    new Employee { Id = 2, Name = "Bob" },
9    new Employee { Id = 1, Name = "Alicia" }
10};
11
12var distinct = employees.DistinctBy(e => e.Id).ToList();
13
14foreach (var employee in distinct)
15{
16    Console.WriteLine($"{employee.Id} - {employee.Name}");
17}

This keeps the first element encountered for each distinct key value.

Older Fallback: GroupBy

If DistinctBy is unavailable, GroupBy is a readable fallback.

csharp
1var distinct = employees
2    .GroupBy(e => e.Id)
3    .Select(group => group.First())
4    .ToList();

The effect is similar: group by the property, then choose one representative from each group.

This is often simpler than writing a whole comparer when you only need the logic in one place.

Custom Comparer for Reuse

If the same distinct-by-property rule appears throughout the codebase, a custom IEqualityComparer<T> can be worth the extra code.

csharp
1using System.Collections.Generic;
2
3public class EmployeeIdComparer : IEqualityComparer<Employee>
4{
5    public bool Equals(Employee? x, Employee? y)
6    {
7        if (ReferenceEquals(x, y)) return true;
8        if (x is null || y is null) return false;
9        return x.Id == y.Id;
10    }
11
12    public int GetHashCode(Employee obj)
13    {
14        return obj.Id.GetHashCode();
15    }
16}

Then:

csharp
var distinct = employees.Distinct(new EmployeeIdComparer()).ToList();

This is useful when the equality rule is part of the domain and should be reused explicitly.

Do Not Override Equality Casually

You could override Equals and GetHashCode on the entity itself, but that changes equality for the whole application. That is only appropriate when the property truly defines the object's identity everywhere.

If Employee.Id is a database identity and that is your domain-wide notion of equality, overriding may be reasonable. If not, use DistinctBy, GroupBy, or a comparer instead of changing global object behavior.

Which Duplicate Survives

One subtle point is that property-based distinct operations typically keep the first item encountered for each key. That means order matters.

If you want "the newest record for each Id" rather than "the first record for each Id," sort first or choose the right group representative:

csharp
1var latestPerId = employees
2    .OrderByDescending(e => e.Name)
3    .DistinctBy(e => e.Id)
4    .ToList();

The property selector defines uniqueness, but the input order often determines which item is preserved.

Common Pitfalls

The biggest mistake is assuming Distinct() can infer which property should define equality. It cannot; it only knows about element equality.

Another issue is overriding Equals and GetHashCode just to satisfy one query. That can create surprising behavior everywhere else in the application.

Developers also forget that property-based distinct keeps one representative, not a merged record. If duplicates carry different non-key data, be deliberate about which one survives.

Finally, if DistinctBy is unavailable in your target framework, do not force awkward workarounds. GroupBy(...).Select(g => g.First()) is perfectly serviceable.

Summary

  • Plain Distinct() uses element equality, not a chosen property selector.
  • 'DistinctBy is the cleanest modern way to remove duplicates by one property.'
  • 'GroupBy(...).Select(g => g.First()) is a good fallback on older frameworks.'
  • Use a custom comparer when the rule should be reused across the codebase.
  • Be explicit about which duplicate record you want to keep.

Course illustration
Course illustration

All Rights Reserved.