从列表中删除具有重复属性的对象
本文关键字:属性 对象 列表 删除 | 更新日期: 2023-09-27 18:31:27
我有一个C#中的对象列表。 所有对象都包含属性部门和课程。
有几个对象具有相同的部门和课程。
如何修剪列表(或创建一个新列表),其中每个唯一(部门和课程)属性只有一个对象。
[任何其他重复项将从列表中删除]
我知道如何使用单个属性执行此操作:
fooList.GroupBy(x => x.dept).Select(x => x.First());
但是,我想知道如何为多个属性(2 个或更多)执行此操作?
若要使用多个属性,可以使用匿名类型:
var query = fooList.GroupBy(x => new { x.Dept, x.Course })
.Select(x => x.First());
当然,这取决于Dept
和Course
确定平等的类型。或者,您的类可以实现IEqualityComparer<T>
然后您可以使用接受比较器的 Enumerable.Distinct
方法。
另一种方法是将 LINQ Distinct
扩展方法与 IEqualityComparer<Foo>
一起使用。它要求您实现比较器;但是,后者是可重用和可测试的。
public class FooDeptCourseEqualityComparer : IEqualityComparer<Foo>
{
public bool Equals(Foo x, Foo y)
{
return
x.Dept == y.Dept &&
x.Course.ToLower() == y.Course.ToLower();
}
public int GetHashCode(Foo obj)
{
unchecked {
return 527 + obj.Dept.GetHashCode() * 31 + obj.Course.GetHashCode();
}
}
#region Singleton Pattern
public static readonly FooDeptCourseEqualityComparer Instance =
new FooDeptCourseEqualityComparer();
private FooDeptCourseEqualityComparer() { }
#endregion
}
我的示例使用单例模式。由于该类没有任何状态信息,因此我们不需要在每次使用它时都创建一个新实例。
我的代码不处理null
值。当然,如果它们可能发生,您必须处理它们。
唯一值返回如下
var result = fooList.Distinct(FooDeptCourseEqualityComparer.Instance);
更新
我建议使用一个通用的 EqualityComparer 类,该类在构造函数中接受 lambda 表达式,并且可以在多种情况下重用
public class LambdaEqualityComparer<T> : IEqualityComparer<T>
{
private Func<T, T, bool> _areEqual;
private Func<T, int> _getHashCode;
public LambdaEqualityComparer(Func<T, T, bool> areEqual,
Func<T, int> getHashCode)
{
_areEqual = areEqual;
_getHashCode = getHashCode;
}
public LambdaEqualityComparer(Func<T, T, bool> areEqual)
: this(areEqual, obj => obj.GetHashCode())
{
}
#region IEqualityComparer<T> Members
public bool Equals(T x, T y)
{
return _areEqual(x, y);
}
public int GetHashCode(T obj)
{
return _getHashCode(obj);
}
#endregion
}
你可以这样使用它
var comparer = new LambdaEqualityComparer<Foo>(
(x, y) => x.Dept == y.Dept && x.Course == y.Course,
obj => {
unchecked {
return 527 + obj.Dept.GetHashCode() * 31 + obj.Course.GetHashCode();
}
}
);
var result = fooList.Distinct(comparer);
注意:您必须提供哈希代码的计算,因为Distinct
使用内部Set<T>
类,而内部类又使用哈希代码。
更新 #2
更通用的相等比较器会自动实现比较并接受属性访问器列表;但是,您无法控制如何执行比较。
public class AutoEqualityComparer<T> : IEqualityComparer<T>
{
private Func<T, object>[] _propertyAccessors;
public AutoEqualityComparer(params Func<T, object>[] propertyAccessors)
{
_propertyAccessors = propertyAccessors;
}
#region IEqualityComparer<T> Members
public bool Equals(T x, T y)
{
foreach (var getProp in _propertyAccessors) {
if (!getProp(x).Equals(getProp(y))) {
return false;
}
}
return true;
}
public int GetHashCode(T obj)
{
unchecked {
int hash = 17;
foreach (var getProp in _propertyAccessors) {
hash = hash * 31 + getProp(obj).GetHashCode();
}
return hash;
}
}
#endregion
}
用法
var comparer = new AutoEqualityComparer<Foo>(foo => foo.Dept,
foo => foo.Course);
var result = fooList.Distinct(comparer);