How to get distinct values from the List<T> with LINQ
- by Vincent Maverick Durano
Recently I was working with data from a generic List<T> and one of my objectives is to get the distinct values that is found in the List. Consider that we have this simple class that holds the following properties: public class Product
{
public string Make { get; set; }
public string Model { get; set; }
}
Now in the page code behind we will create a list of product by doing the following:
private List<Product> GetProducts() {
List<Product> products = new List<Product>();
Product p = new Product();
p.Make = "Samsung";
p.Model = "Galaxy S 1";
products.Add(p);
p = new Product();
p.Make = "Samsung";
p.Model = "Galaxy S 2";
products.Add(p);
p = new Product();
p.Make = "Samsung";
p.Model = "Galaxy Note";
products.Add(p);
p = new Product();
p.Make = "Apple";
p.Model = "iPhone 4";
products.Add(p);
p = new Product();
p.Make = "Apple";
p.Model = "iPhone 4s";
products.Add(p);
p = new Product();
p.Make = "HTC";
p.Model = "Sensation";
products.Add(p);
p = new Product();
p.Make = "HTC";
p.Model = "Desire";
products.Add(p);
p = new Product();
p.Make = "Nokia";
p.Model = "Some Model";
products.Add(p);
p = new Product();
p.Make = "Nokia";
p.Model = "Some Model";
products.Add(p);
p = new Product();
p.Make = "Sony Ericsson";
p.Model = "800i";
products.Add(p);
p = new Product();
p.Make = "Sony Ericsson";
p.Model = "800i";
products.Add(p);
return products;
}
And then let’s bind the products to the GridView.
protected void Page_Load(object sender, EventArgs e) {
if (!IsPostBack) {
Gridview1.DataSource = GetProducts();
Gridview1.DataBind();
}
}
Running the code will display something like this in the page:
Now what I want is to get the distinct row values from the list. So what I did is to use the LINQ Distinct operator and unfortunately it doesn't work. In order for it work is you must use the overload method of the Distinct operator for you to get the desired results. So I’ve added this IEqualityComparer<T> class to compare values:
class ProductComparer : IEqualityComparer<Product>
{
public bool Equals(Product x, Product y) {
if (Object.ReferenceEquals(x, y)) return true;
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
return x.Make == y.Make && x.Model == y.Model;
}
public int GetHashCode(Product product) {
if (Object.ReferenceEquals(product, null)) return 0;
int hashProductName = product.Make == null ? 0 : product.Make.GetHashCode();
int hashProductCode = product.Model.GetHashCode();
return hashProductName ^ hashProductCode;
}
}
After that you can then bind the GridView like this:
protected void Page_Load(object sender, EventArgs e) {
if (!IsPostBack) {
Gridview1.DataSource = GetProducts().Distinct(new ProductComparer());
Gridview1.DataBind();
}
}
Running the page will give you the desired output below:
As you notice, it now eliminates the duplicate rows in the GridView. Now what if we only want to get the distinct values for a certain field. For example I want to get the distinct “Make” values such as Samsung, Apple, HTC, Nokia and Sony Ericsson and populate them to a DropDownList control for filtering purposes. I was hoping the the Distinct operator has an overload that can compare values based on the property value like (GetProducts().Distinct(o => o.PropertyToCompare). But unfortunately it doesn’t provide that overload so what I did as a workaround is to use the GroupBy,Select and First LINQ query operators to achieve what I want.
Here’s the code to get the distinct values of a certain field.
protected void Page_Load(object sender, EventArgs e) {
if (!IsPostBack) {
DropDownList1.DataSource = GetProducts().GroupBy(o => o.Make).Select(o => o.First());
DropDownList1.DataTextField = "Make";
DropDownList1.DataValueField = "Model";
DropDownList1.DataBind();
}
}
Running the code will display the following output below:
That’s it! I hope someone find this post useful!