Description
Reflection is a great tool when it comes to handling the information at runtime. I found this solution below to be suitable in one of my applications because I wanted something that I could easily adapt.
The implementation of the method below looks for both public and private fields and properties.
public static partial class Extensions { public static IEnumerable DistinctBy<T, Tkey>(this IEnumerable @this, Expression<Func<T, object>> expression) { string binding = ReflectObj.ObjGetPropertyPath(expression); return DistinctBy<T, Tkey>(@this, binding); } public static IEnumerable DistinctBy<T, Tkey>(this IEnumerable @this, string binding) { string[] _params = binding.Split('.'); IList list = new List(); IList ReturnList = new List(); // foreach (var item in @this) { //get key object Key = item; foreach (var _param in _params) { Type t = Key.GetType(); PropertyInfo pi = t.GetProperty(_param, BindingFlags.Public | BindingFlags.Instance); Key = pi.GetValue(Key); } if (list.Where(x => x.Equals(Key)).Count() == 0) { list.Add((Tkey)Key); ReturnList.Add(item); } list.Add((Tkey)Key); } return ReturnList; } }
Usage
Let’s create a product class that has a Price. We aim to easily create a list with unique “parameters” that will be defined in a string.
class Price { public double Value { get; set; } } class Product { public string Name { get; set; } public Price Price { get; set; } private int NoPrice; } class Program { static void Main(string[] args) { List Products = new List { new Product{ Name = "Product1", Price = new Price{ Value = 100} }, new Product{ Name = "Product2", Price = new Price{ Value = 100} }, new Product{ Name = "Product3", Price = new Price{ Value = 200} } }; // will result 1 item, NoPrice default for all is 0 var DistinctProducts1 = Products.DistinctBy<Product, int>("NoPrice"); // will result 3 items, no such key is found var DistinctProducts2 = Products.DistinctBy<Product, int>("NoPrice.Test"); // will result 2 items - Value is 100 for 2 of them var DistinctProducts3 = Products.DistinctBy<Product, int>("Price.Value"); } } }