LINQ Distinct Method in C#

LINQ Distinct Method in C# with Examples

In this article, I am going to discuss the LINQ Distinct Method in C# using examples. Please read our previous article where we discussed the basics of LINQ Set Operators. At the end of this article, you will understand the following pointers related to LINQ Distinct Operations.

  1. What is LINQ Distinct Method in C#?
  2. Examples of LINQ Distinct Method using both Method and Query Syntax
  3. How to implement IEqualityComparer?
What us LINQ Distinct Method in C#?

The LINQ Distinct Method in C# is used to return the distinct elements from a single data source. There are two overloaded versions available for the Distinct Method as shown below.

LINQ Distinct Method in C# with Examples

The one and the only difference between these two methods is the second overloaded version takes an IEqualityComparer as input that means the Distinct Operator can also be used with Comparer also. If this is not clear at the moment, don’t worry we will cover the use of the Comparer in this article also.

Example1: LINQ Distinct Method on Value Type

Here we have an integer collection that contains duplicate integer values. Our requirement is to fetch remove the duplicate values and return only the distinct values as shown below.

LINQ Distinct Method on Value Type

The following program shows how to get the distinct integer values using both Method and Query syntax:

using System;
using System.Collections.Generic;
using System.Linq;

namespace LINQDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            List<int> intCollection = new List<int>()
            {
                1,2,3,2,3,4,4,5,6,3,4,5
            };

            //Using Method Syntax
            var MS = intCollection.Distinct();

            //Using Query Syntax
            var QS = (from num in intCollection
                      select num).Distinct();
            foreach (var item in MS)
            {
                Console.WriteLine(item);
            }

            Console.ReadKey();
        }
    }
}

Output:

Note: In query syntax, there is no such operator call distinct, so here we need to use both query and method syntax to achieve the same.

Example2: 

Here we have a string array of names and we need to return the distinct names from the collection.

using System;
using System.Linq;

namespace LINQDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] namesArray = { "Priyanka", "HINA", "hina", "Anurag", "Anurag", "ABC", "abc" };

            var distinctNames = namesArray.Distinct();

            foreach (var name in distinctNames)
            {
                Console.WriteLine(name);
            }
            
            Console.ReadKey();
        }
    }
}

When we execute the above program, it gives us the below output.

As you can see the name Hina and Abc have appeared twice. This is because the default comparer, which is used to filter the duplicate values is case-sensitive.

If you want to make the comparison to be case-insensitive then you need to use the other overloaded version which takes IEqualityComparer as an argument. So here we need to pass a class which must implement the IEqualityComparer interface.

So let’s modify the program as shown below. As you can see here we are passing StringComparer as an argument to the Distinct method.

using System;
using System.Linq;

namespace LINQDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] namesArray = { "Priyanka", "HINA", "hina", "Anurag", "Anurag", "ABC", "abc" };

            var distinctNames = namesArray.Distinct(StringComparer.OrdinalIgnoreCase);

            foreach (var name in distinctNames)
            {
                Console.WriteLine(name);
            }
            
            Console.ReadKey();
        }
    }
}

Now run the application and it should display the distinct names as shown below.

If we go to the definition of StringComparer class then we can observe that this class implements the IEqualityComparer interface as shown below.

StringComparer class Definition in C#

LINQ Distinct Operation with Complex Type:

The LINQ Distinct Method works in a different manner with complex types like Employee, Product, Student, etc. Let us understand with an example. We are going to work with the following Student data.

LINQ Distinct Operation with Complex Type

Create a class file with the name Student.cs and then copy and paste the following code.

using System.Collections.Generic;
namespace LINQDemo
{
    public class Student
    {
        public int ID { get; set; }
        public string Name { get; set; }

        public static List<Student> GetStudents()
        {
            List<Student> students = new List<Student>()
            {
                new Student {ID = 101, Name = "Preety" },
                new Student {ID = 102, Name = "Sambit" },
                new Student {ID = 103, Name = "Hina"},
                new Student {ID = 104, Name = "Anurag"},
                new Student {ID = 102, Name = "Sambit"},
                new Student {ID = 103, Name = "Hina"},
                new Student {ID = 101, Name = "Preety" },
            };

            return students;
        }
    }
}

Here we created the student class with the required properties. Along the same way, we have GetStudents() method which will return all the students.

Example3:

Here we need to fetch all the distinct names from the student’s collection. The following programs show how to achieve this using both Method and Query syntax.

using System;
using System.Linq;

namespace LINQDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            //Using Method Syntax
            var MS = Student.GetStudents()
                    .Select(std => std.Name)
                    .Distinct().ToList();

            //Using Query Syntax
            var QS = (from std in Student.GetStudents()
                      select std.Name)
                      .Distinct().ToList();

            foreach(var item in MS)
            {
                Console.WriteLine(item);
            }

            Console.ReadKey();
        }
    }
}

Output:

Example4:

Now we need to select distinct students from the collection. As you can see in our collection three students are identical and in our result set, they should appear only once. Let us modify the program class as shown below.

using System;
using System.Linq;

namespace LINQDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            //Using Method Syntax
            var MS = Student.GetStudents()
                    .Distinct().ToList();

            //Using Query Syntax
            var QS = (from std in Student.GetStudents()
                      select std)
                      .Distinct().ToList();

            foreach (var item in QS)
            {
                Console.WriteLine($"ID : {item.ID} , Name : {item.Name} ");
            }

            Console.ReadKey();
        }
    }
}

Now execute the query and see the output.

As you can see, it will not select distinct students rather it select all the students. This is because the default comparer which is used for comparison is only checked whether two object references are equal and not the individual property values of the complex object.

How to solve the above problem?

We can solve the above problem in four different ways. They are as follows

  1. We need to use the other overloaded version of Distinct() method which takes IEqualityComparer interface as an argument. So here we will create a class that implements IEqualityComparer interface and then we need to pass that compare instance to the Distinct() method.
  2. In the second approach, we need to override the Equals() and GetHashCode() methods within the Student class itself.
  3. In the third approach, we need to project the required properties into a new anonymous type, which already overrides the Equals() and GetHashCode() methods
  4. By Implementing IEquatable<T> interface.
Approach1: Implementing IEqualityComparer interface

Create a class file with the name StudentComparer.cs and then implement the IEqualityComparer interface as shown below.

using System.Collections.Generic;

namespace LINQDemo
{
    public class StudentComparer : IEqualityComparer<Student>
    {
        public bool Equals(Student x, Student y)
        {
            //First check if both object reference are equal then return true
            if(object.ReferenceEquals(x, y))
            {
                return true;
            }

            //If either one of the object refernce is null, return false
            if (object.ReferenceEquals(x,null) || object.ReferenceEquals(y, null))
            {
                return false;
            }

            //Comparing all the properties one by one
            return x.ID == y.ID && x.Name == y.Name;
        }

        public int GetHashCode(Student obj)
        {
            //If obj is null then return 0
            if (obj == null)
            {
                return 0;
            }

            //Get the ID hash code value
            int IDHashCode = obj.ID.GetHashCode();

            //Get the string HashCode Value
            //Check for null refernece exception
            int NameHashCode = obj.Name == null ? 0 : obj.Name.GetHashCode();

            return IDHashCode ^ NameHashCode;
        }
    }
}

As you can here we implements the IEqualityComparer<T> interface and then implements the Equals() and GetHashCode() methods. Now we need to create an instance of StudentComparer class and then we need to pass that instance to the Distinct method as shown in the below program.

using System;
using System.Linq;

namespace LINQDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            //Creating an instance of StudentComparer
            StudentComparer studentComparer = new StudentComparer();
            
            //Using Method Syntax
            var MS = Student.GetStudents()
                    .Distinct(studentComparer).ToList();

            //Using Query Syntax
            var QS = (from std in Student.GetStudents()
                      select std)
                      .Distinct(studentComparer).ToList();

            foreach (var item in QS)
            {
                Console.WriteLine($"ID : {item.ID} , Name : {item.Name} ");
            }

            Console.ReadKey();
        }
    }
}

Now run the application and it will display the distinct students as shown below.

Approach2: Overriding the Equals() and GetHashCode() methods

Now, we need to override the Equals() and GetHashCode() methods within the Student class. So, modify the Student class as shown below.

using System.Collections.Generic;
namespace LINQDemo
{
    public class Student
    {
        public int ID { get; set; }
        public string Name { get; set; }

        public static List<Student> GetStudents()
        {
            List<Student> students = new List<Student>()
            {
                new Student {ID = 101, Name = "Preety" },
                new Student {ID = 102, Name = "Sambit" },
                new Student {ID = 103, Name = "Hina"},
                new Student {ID = 104, Name = "Anurag"},
                new Student {ID = 102, Name = "Sambit"},
                new Student {ID = 103, Name = "Hina"},
                new Student {ID = 101, Name = "Preety" },
            };

            return students;
        }

        public override bool Equals(object obj)
        {
            //As the obj parameter type id object, so we need to
            //cast it to Student Type
            return this.ID == ((Student)obj).ID && this.Name == ((Student)obj).Name;
        }

        public override int GetHashCode()
        {
            //Get the ID hash code value
            int IDHashCode = this.ID.GetHashCode();

            //Get the string HashCode Value
            //Check for null refernece exception
            int NameHashCode = this.Name == null ? 0 : this.Name.GetHashCode();

            return IDHashCode ^ NameHashCode;
        }
    }
}

With the above changes in Student class, modify the Main method as shown below.

using System;
using System.Linq;

namespace LINQDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            //Using Method Syntax
            var MS = Student.GetStudents()
                    .Distinct().ToList();

            //Using Query Syntax
            var QS = (from std in Student.GetStudents()
                      select std)
                      .Distinct().ToList();

            foreach (var item in MS)
            {
                Console.WriteLine($"ID : {item.ID} , Name : {item.Name} ");
            }

            Console.ReadKey();
        }
    }
}

Now execute the program and it will display the distinct records as expected.

Approach3: Using Anonymous Type

Here we need to project the properties of Student class into a new anonymous type, which already overrides the Equals() and GetHashCode() methods.

using System;
using System.Linq;

namespace LINQDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            //Using Method Syntax
            var MS = Student.GetStudents()
                    .Select(std => new { std.ID, std.Name})
                    .Distinct().ToList();

            //Using Query Syntax
            var QS = (from std in Student.GetStudents()
                      select std)
                      .Select(std => new { std.ID, std.Name })
                      .Distinct().ToList();

            foreach (var item in MS)
            {
                Console.WriteLine($"ID : {item.ID} , Name : {item.Name} ");
            }

            Console.ReadKey();
        }
    }
}

In the above example, we project the ID and Name properties to IEnumeable<’a> means to anonymous type which already overrides the Equals and GetHashCode method. Now run the application and you will see the output as expected.

Approach4: Implementing the IEquatble<T> interface in Student Class.

Modify the Student class as shown below.

using System.Collections.Generic;
namespace LINQDemo
{
    public class Student : IEquatable<Student>
    {
        public int ID { get; set; }
        public string Name { get; set; }

        public static List<Student> GetStudents()
        {
            List<Student> students = new List<Student>()
            {
                new Student {ID = 101, Name = "Preety" },
                new Student {ID = 102, Name = "Sambit" },
                new Student {ID = 103, Name = "Hina"},
                new Student {ID = 104, Name = "Anurag"},
                new Student {ID = 102, Name = "Sambit"},
                new Student {ID = 103, Name = "Hina"},
                new Student {ID = 101, Name = "Preety" },
            };

            return students;
        }

        public bool Equals(Student other)
        {
            if (object.ReferenceEquals(other, null))
            {
                return false;
            }

            if (object.ReferenceEquals(this, other))
            {
                return true;
            }

            return this.ID.Equals(other.ID) && this.Name.Equals(other.Name);
        }

        public override int GetHashCode()
        {
            int IDHashCode = this.ID.GetHashCode();
            int NameHashCode = this.Name == null ? 0 : this.Name.GetHashCode();

            return IDHashCode ^ NameHashCode;
        }
    }
}

As you can see, here we have done two things. First, we implement the Equals method of the IEquatable interface and then override the GetHashCode method.

Now change the Program class as shown below.

using System;
using System.Linq;

namespace LINQDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            //Using Method Syntax
            var MS = Student.GetStudents()
                    .Distinct().ToList();

            //Using Query Syntax
            var QS = (from std in Student.GetStudents()
                      select std)
                      .Distinct().ToList();

            foreach (var item in MS)
            {
                Console.WriteLine($"ID : {item.ID} , Name : {item.Name} ");
            }

            Console.ReadKey();
        }
    }
}

Run the application and see the output as expected.

Difference between IEqualityComparer<T> and IEquatable<T>:

The IEqualityComparer<T> is an interface for an object that performs the comparison on two objects of the type T whereas the IEquatable<T> is also an interface for an object of type T so that it can compare itself to another.

In the next article, I am going to discuss the Except Method in LINQ with examples. I hope this article gives you a very good understanding of the Concept LINQ Distinct operation in C# with different kinds of examples.

Leave a Reply

Your email address will not be published. Required fields are marked *