如何在多个字段中使用LINQ Distinct()
我有从数据库派生的以下EF类 (简化)
class Product { public string ProductId; public string ProductName; public string CategoryId; public string CategoryName; }
ProductId
是表格的主键 。
对于由数据库devise人员做出的糟糕的devise决定(我无法修改它),我在这个表中有CategoryId
和CategoryName
。
我需要一个DropDownList (独特) CategoryId
作为值和CategoryName
作为文本 。 所以我申请了下面的代码:
product.Select(m => new {m.CategoryId, m.CategoryName}).Distinct();
这在逻辑上应该创build一个具有CategoryId
和CategoryName
属性的匿名对象。 Distinct()
保证没有重复对( CategoryId
, CategoryName
)。
但实际上这是行不通的。 就我所了解的Distinct()
作品,只有一个字段在集合中,否则它只是忽略它们…是否正确? 有没有解决办法? 谢谢!
UPDATE
对不起, product
是:
List<Product> product = new List<Product>();
我发现了一个替代方法来获得与Distinct()
相同的结果:
product.GroupBy(d => new {d.CategoryId, d.CategoryName}) .Select(m => new {m.Key.CategoryId, m.Key.CategoryName})
我假设你使用不同的方法调用列表。 您需要使用查询的结果作为DropDownList的数据源,例如通过ToList
实现它。
var distinctCategories = product .Select(m => new {m.CategoryId, m.CategoryName}) .Distinct() .ToList(); DropDownList1.DataSource = distinctCategories; DropDownList1.DataTextField = "CategoryName"; DropDownList1.DataValueField = "CategoryId";
Distinct()保证没有重复对(CategoryId,CategoryName)。
– 完全一样
匿名types“神奇地”实现Equals
和GetHashcode
我假设另一个错误的地方。 区分大小写? 可变类? 非可比较的领域?
不同的方法从一个序列中返回不同的元素。
如果你用Reflector看看它的实现,你会看到它为你的匿名types创buildDistinctIterator
。 枚举集合时,不同的迭代器将元素添加到Set
。 这个枚举器将跳过已经在Set
所有元素。 Set
使用GetHashCode
和Equals
方法来定义元素是否已经存在于Set
。
GetHashCode
和Equals
如何实现匿名types? 正如它在msdn上所说:
匿名types的Equals和GetHashCode方法是根据属性的Equals和GetHashcode方法定义的,相同匿名types的两个实例只有在它们的所有属性相等时才相等。
所以,当迭代不同的集合时,你肯定应该有不同的匿名对象。 结果并不取决于您为匿名types使用多less个字段。
在你的select使用Key
关键字将工作,如下所示。
product.Select(m => new {Key m.CategoryId, Key m.CategoryName}).Distinct();
我意识到这是提出一个老线索,但认为这可能会帮助一些人。 我一般使用.NET编写VB.NET代码,所以Key
可能会以不同的方式转换成C#。
回答这个问题的标题(什么吸引了这里的人),并忽略了这个例子使用匿名types….
此解决scheme也适用于非匿名types。 匿名types不应该被需要。
帮手类:
/// <summary> /// Allow IEqualityComparer to be configured within a lambda expression. /// From https://stackoverflow.com/questions/98033/wrap-a-delegate-in-an-iequalitycomparer /// </summary> /// <typeparam name="T"></typeparam> public class LambdaEqualityComparer<T> : IEqualityComparer<T> { readonly Func<T, T, bool> _comparer; readonly Func<T, int> _hash; /// <summary> /// Simplest constructor, provide a conversion to string for type T to use as a comparison key (GetHashCode() and Equals(). /// https://stackoverflow.com/questions/98033/wrap-a-delegate-in-an-iequalitycomparer, user "orip" /// </summary> /// <param name="toString"></param> public LambdaEqualityComparer(Func<T, string> toString) : this((t1, t2) => toString(t1) == toString(t2), t => toString(t).GetHashCode()) { } /// <summary> /// Constructor. Assumes T.GetHashCode() is accurate. /// </summary> /// <param name="comparer"></param> public LambdaEqualityComparer(Func<T, T, bool> comparer) : this(comparer, t => t.GetHashCode()) { } /// <summary> /// Constructor, provide a equality comparer and a hash. /// </summary> /// <param name="comparer"></param> /// <param name="hash"></param> public LambdaEqualityComparer(Func<T, T, bool> comparer, Func<T, int> hash) { _comparer = comparer; _hash = hash; } public bool Equals(T x, T y) { return _comparer(x, y); } public int GetHashCode(T obj) { return _hash(obj); } }
最简单的用法:
List<Product> products = duplicatedProducts.Distinct( new LambdaEqualityComparer<Product>(p => String.Format("{0}{1}{2}{3}", p.ProductId, p.ProductName, p.CategoryId, p.CategoryName)) ).ToList();
最简单(但效率不高)的用法是映射到string表示,以避免自定义哈希。 相等的string已经有相同的散列码。
参考:
将一个委托包装在IEqualityComparer中
Employee emp1 = new Employee() { ID = 1, Name = "Narendra1", Salary = 11111, Experience = 3, Age = 30 };Employee emp2 = new Employee() { ID = 2, Name = "Narendra2", Salary = 21111, Experience = 10, Age = 38 }; Employee emp3 = new Employee() { ID = 3, Name = "Narendra3", Salary = 31111, Experience = 4, Age = 33 }; Employee emp4 = new Employee() { ID = 3, Name = "Narendra4", Salary = 41111, Experience = 7, Age = 33 }; List<Employee> lstEmployee = new List<Employee>(); lstEmployee.Add(emp1); lstEmployee.Add(emp2); lstEmployee.Add(emp3); lstEmployee.Add(emp4); var eemmppss=lstEmployee.Select(cc=>new {cc.ID,cc.Age}).Distinct();