比较两个列表并忽略特定属性

时间:2012-03-23 02:29:42

标签: c# list generics comparison iequalitycomparer

我有两个员工名单,我想从中获取唯一的记录,但这有一个扭曲。每个列表中都有一个Employee类:

public class  Employee
{

// I want to completely ignore ID in the comparison
public int ID{ get; set; }
// I want to use FirstName and LastName in comparison
public string FirstName{ get; set; }
public string LastName{ get; set; }
}

我想要比较匹配的唯一属性是FirstName和LastName。我想在比较中完全忽略ID。 allFulltimeEmployees列表中有3名员工,allParttimeEmployees列表中有3名员工。名单上的两个项目的名字和姓氏相匹配 - 莎莉琼斯和弗雷德杰克逊。列表中有一个项不匹配,因为FirstName是相同的,但LastName不同:

emp.id = null; // not populated or used in comparison
emp.FirstName = "Joe"; // same
emp.LastName = "Smith"; // different

allFulltimeEmployees.Add(emp);

emp.id = 3; // not used in comparison
emp.FirstName = "Joe"; // a match
emp.LastName = "Williams"; // not a match - different last name

allParttimeEmployees.Add(emp);

所以我想在比较两个列表时忽略类中的ID属性。我想将乔·威廉姆斯标记为不匹配,因为两个名单中史密斯和威廉姆斯的姓氏不匹配。

// finalResult should only have Joe Williams in it 

var finalResult = allFulltimeEmployees.Except(allParttimeEmployees);

我尝试过使用IEqualityComparer但它不起作用,因为它在参数中使用单个Employee类而不是IEnumerable列表:

public class EmployeeEqualityComparer : IEqualityComparer<Employee>
    {
        public bool Equals(Employee x, Employee y)
        {
            if (x.FirstName == y.FirstName && x.LastName == y.LastName)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        public int GetHashCode(Employee obj)
        {
            return obj.GetHashCode();
        }
    }

如何成功完成我想要的操作并执行此操作?谢谢你的帮助!

5 个答案:

答案 0 :(得分:12)

您使用IEqualityComparer的想法很好,这是您的执行错误。值得注意的是,您的GetHashCode方法。

public int GetHashCode(Employee obj) 
{ 
    return obj.GetHashCode(); 
} 

IEqualityComparer定义EqualsGetHashCode ,因为两者都很重要。实施此界面时不要忽略GetHashCode!它在平等比较中起着关键作用。不,这并不表示两个项目相同,但它表明两个元素不是。两个相等的元素必须返回相同的哈希码。如果他们不这样做,他们就不能被认为是平等的。如果他们这样做,那么他们可能是平等的,而等式函数只有继续探索Equals

将实现委托给实际雇员对象的GetHashCode方法,您依赖于Employee类使用的实现。 只有该实现被覆盖才会对您有用,仅当使用您的密钥字段时才会有用。 如果它是,那么你很可能首先不需要定义自己的外部比较器!

构建一个GetHashCode方法,该方法会影响您的关键字段,并且您将被设置。

public int GetHashCode(Employee obj)
{
     // null handling omitted for brevity, but you will want to
     // handle null values appropriately

     return obj.FirstName.GetHashCode() * 117 
          + obj.LastName.GetHashCode(); 
}

准备好此方法后,请在调用Except时使用比较器。

var comparer = new EmployeeEqualityComparer();
var results = allFulltimeEmployees.Except(allParttimeEmployees, comparer);

答案 1 :(得分:3)

您可以在Equals课程中覆盖GetHashCodeEmployees

例如,

    public class Employee
    {

        // I want to completely ignore ID in the comparison
        public int ID { get; set; }
        // I want to use FirstName and LastName in comparison
        public string FirstName { get; set; }
        public string LastName { get; set; }

        public override bool Equals(object obj)
        {
            var other = obj as Employee;
            return this.FirstName == other.FirstName && this.LastName == other.LastName;
        }

        public override int GetHashCode()
        {
            return this.FirstName.GetHashCode() ^ this.LastName.GetHashCode();
        }
    }

我测试了以下数据集:

var empList1 = new List<Employee>
{
    new Employee{ID = 1, FirstName = "D", LastName = "M"}, 
    new Employee{ID = 2, FirstName = "Foo", LastName = "Bar"}
};
var empList2 = new List<Employee> 
{ 
    new Employee { ID = 2, FirstName = "D", LastName = "M" }, 
    new Employee { ID = 1, FirstName = "Foo", LastName = "Baz" } 
};

var result = empList1.Except(empList2); // Contained "Foo Bar", ID #2.

答案 2 :(得分:0)

您的IEqualityComparer应该有效:

var finalResult = allFulltimeEmployees.Except(allParttimeEmployees, new EmployeeEqualityComparer());

答案 3 :(得分:0)

尝试为Employee课程实施IEquatable(T)界面。您只需要为Equals()方法提供一个实现,您可以根据需要定义该方法(即忽略员工ID)。

  

IEquatable接口由泛型集合对象使用   测试时作为Dictionary,List和LinkedList   在Contains,IndexOf,LastIndexOf和。等方法中实现相等   去掉。它应该针对可能存储的任何对象实现   在通用集合中。

Equals()方法的示例实现:

public bool Equals(Employee other)
{
   return (other != null) && (FirstName == other.FirstName) && (LastName == other.LastName);
}

答案 4 :(得分:0)

这不是最优雅的解决方案,但你可以制作这样的功能

public string GetKey(Employee emp)
{
    return string.Format("{0}#{1}", emp.FirstName, emp.LastName)
}

然后将allFullTimeEmployees中的所有内容填充到Dictionary<string, Employee>中,其中字典的键是在每个员工对象上调用GetKey的结果。然后你可以循环allParttimeEmployees并在每个上面调用GetKey,探究字典(例如使用TryGetValueContainsKey),并采取任何必要的行动。重复,例如从字典中删除副本。